diff --git a/Cargo.lock b/Cargo.lock index cfdd873e80fab..ee6726eae514d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4158,7 +4158,6 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", - "rustc_serialize", "rustc_type_ir", "rustc_type_ir_macros", "tracing", 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 4423edb060518..207c9e84cc004 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 83a9827e8f81f..8714df5ae8a49 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, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use smallvec::SmallVec; use tracing::{debug, instrument}; +use type_check::free_region_relations::UniversalRegionRelations; +use type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults}; use crate::borrow_set::{BorrowData, BorrowSet}; -use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions}; +use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions, RustcFacts}; use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows}; use crate::diagnostics::{ AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName, @@ -62,8 +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,12 +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); - let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } = - do_mir_borrowck(&mut root_cx, def, None).0; - debug_assert!(closure_requirements.is_none()); - debug_assert!(used_mut_upvars.is_empty()); - root_cx.finalize() + let root_cx = BorrowCheckRootCtxt::new(tcx, def); + root_cx.borrowck_root(None).0 } } @@ -143,6 +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 @@ -281,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 @@ -289,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); @@ -314,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); @@ -324,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, @@ -333,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); @@ -568,12 +673,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 f8af9e59f6357..6fb753619be0c 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; @@ -166,20 +163,6 @@ pub struct RegionInferenceContext<'tcx> { /// compute the values of each region. constraint_sccs: ConstraintSccs, - /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if - /// `B: A`. This is used to compute the universal regions that are required - /// to outlive a given SCC. Computed lazily. - rev_scc_graph: Option, - - /// 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>, @@ -196,32 +179,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 @@ -314,7 +271,6 @@ enum Trace<'a, 'tcx> { StartRegion, FromGraph(&'a OutlivesConstraint<'tcx>), FromStatic(RegionVid), - FromMember(RegionVid, RegionVid, Span), NotVisited, } @@ -424,7 +380,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { placeholder_index_to_region: _, liveness_constraints, mut outlives_constraints, - mut member_constraints, universe_causes, type_tests, } = constraints; @@ -438,7 +393,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); @@ -463,18 +417,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_values.merge_liveness(scc, region, &liveness_constraints); } - let member_constraints = - Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r))); - let mut result = Self { definitions, liveness_constraints, constraints, constraint_graph, constraint_sccs, - rev_scc_graph: None, - member_constraints, - member_constraints_applied: Vec::new(), universe_causes, scc_values, type_tests, @@ -632,19 +580,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. @@ -656,7 +591,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { polonius_output: Option>, ) -> (Option>, RegionErrors<'tcx>) { let mir_def_id = body.source.def_id(); - self.propagate_constraints(); + self.scc_values.propagate_constraints(&self.constraint_sccs); let mut errors_buffer = RegionErrors::new(infcx.tcx); @@ -689,12 +624,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() { @@ -708,168 +637,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - /// Propagate the region constraints: this will grow the values - /// for each region variable until all the constraints are - /// satisfied. Note that some values may grow **too** large to be - /// feasible, but we check this later. - #[instrument(skip(self), level = "debug")] - fn propagate_constraints(&mut self) { - debug!("constraints={:#?}", { - let mut constraints: Vec<_> = self.outlives_constraints().collect(); - constraints.sort_by_key(|c| (c.sup, c.sub)); - constraints - .into_iter() - .map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub))) - .collect::>() - }); - - // 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.constraint_sccs().annotation(scc).min_universe().is_root() { - return; - } - - // The existing value for `scc` is a lower-bound. This will - // consist of some set `{P} + {LB}` of points `{P}` and - // lower-bound free regions `{LB}`. As each choice region `O` - // is a free region, it will outlive the points. But we can - // only consider the option `O` if `O: LB`. - choice_regions.retain(|&o_r| { - self.scc_values - .universal_regions_outlived_by(scc) - .all(|lb| self.universal_region_relations.outlives(o_r, lb)) - }); - debug!(?choice_regions, "after lb"); - - // Now find all the *upper bounds* -- that is, each UB is a - // free region that must outlive the member region `R0` (`UB: - // R0`). Therefore, we need only keep an option `O` if `UB: O` - // for all UB. - let universal_region_relations = &self.universal_region_relations; - for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { - debug!(?ub); - choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); - } - debug!(?choice_regions, "after ub"); - - // At this point we can pick any member of `choice_regions` and would like to choose - // it to be a small as possible. To avoid potential non-determinism we will pick the - // smallest such choice. - // - // Because universal regions are only partially ordered (i.e, not every two regions are - // comparable), we will ignore any region that doesn't compare to all others when picking - // the minimum choice. - // - // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where - // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`. - // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`. - let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| { - choice_regions.iter().all(|&r2| { - self.universal_region_relations.outlives(r1, r2) - || self.universal_region_relations.outlives(r2, r1) - }) - }); - // Now we're left with `['static, 'c]`. Pick `'c` as the minimum! - let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| { - let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); - let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); - match (r1_outlives_r2, r2_outlives_r1) { - (true, true) => r1.min(r2), - (true, false) => r2, - (false, true) => r1, - (false, false) => bug!("incomparable regions in total order"), - } - }) else { - debug!("no unique minimum choice"); - return; - }; - - // As we require `'scc: 'min_choice`, we have definitely already computed - // its `scc_values` at this point. - let min_choice_scc = self.constraint_sccs.scc(min_choice); - debug!(?min_choice, ?min_choice_scc); - if self.scc_values.add_region(scc, min_choice_scc) { - self.member_constraints_applied.push(AppliedMemberConstraint { - member_region_scc: scc, - min_choice, - member_constraint_index, - }); - } - } - /// Returns `true` if all the elements in the value of `scc_b` are nameable /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. @@ -1108,7 +875,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; @@ -1654,43 +1420,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("check_bound_universal_region: all bounds satisfied"); } - #[instrument(level = "debug", skip(self, infcx, errors_buffer))] - fn check_member_constraints( - &self, - infcx: &InferCtxt<'tcx>, - errors_buffer: &mut RegionErrors<'tcx>, - ) { - let member_constraints = Rc::clone(&self.member_constraints); - for m_c_i in member_constraints.all_indices() { - debug!(?m_c_i); - let m_c = &member_constraints[m_c_i]; - let member_region_vid = m_c.member_region_vid; - debug!( - ?member_region_vid, - value = ?self.region_value_str(member_region_vid), - ); - let choice_regions = member_constraints.choice_regions(m_c_i); - debug!(?choice_regions); - - // Did the member region wind up equal to any of the option regions? - if let Some(o) = - choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid)) - { - debug!("evaluated as equal to {:?}", o); - continue; - } - - // If not, report an error. - let member_region = ty::Region::new_var(infcx.tcx, member_region_vid); - errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion { - span: m_c.definition_span, - hidden_ty: m_c.hidden_ty, - key: m_c.key, - member_region, - }); - } - } - /// We have a constraint `fr1: fr2` that is not satisfied, where /// `fr2` represents some universal region. Here, `r` is some /// region where we know that `fr1: r` and this function has the @@ -1814,20 +1543,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)); @@ -1874,15 +1589,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 @@ -2205,11 +1911,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. /// @@ -2242,7 +1943,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 550c57338d301..d34c7bc140c65 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,234 +1,691 @@ -use rustc_data_structures::fx::FxIndexMap; +use std::iter; +use std::rc::Rc; + +use rustc_data_structures::frozen::Frozen; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_index::IndexVec; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_macros::extension; +use rustc_middle::bug; +use rustc_middle::mir::ConstraintCategory; +use rustc_middle::ty::relate::{ + Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys, +}; use rustc_middle::ty::{ - self, DefiningScopeKind, 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 super::RegionInferenceContext; -use crate::BorrowCheckRootCtxt; -use crate::session_diagnostics::LifetimeMismatchOpaqueParam; -use crate::universal_regions::RegionClassification; +use super::reverse_sccs::ReverseSccGraph; +use super::values::RegionValues; +use super::{ConstraintSccs, RegionDefinition, RegionInferenceContext}; +use crate::constraints::ConstraintSccIndex; +use crate::consumers::OutlivesConstraint; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::type_check::{Locations, MirTypeckRegionConstraints}; +use crate::universal_regions::{RegionClassification, UniversalRegions}; +use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, BorrowckState}; -impl<'tcx> RegionInferenceContext<'tcx> { - /// Resolve any opaque types that were encountered while borrow checking - /// this item. This is then used to get the type in the `type_of` query. - /// - /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`. - /// This is lowered to give HIR something like - /// - /// type f<'a>::_Return<'_x> = impl Sized + '_x; - /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x } - /// - /// When checking the return type record the type from the return and the - /// type used in the return value. In this case they might be `_Return<'1>` - /// and `&'2 i32` respectively. - /// - /// Once we to this method, we have completed region inference and want to - /// call `infer_opaque_definition_from_instantiation` to get the inferred - /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation` - /// compares lifetimes directly, so we need to map the inference variables - /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`. - /// - /// First we map the regions in the generic parameters `_Return<'1>` to - /// their `external_name` giving `_Return<'a>`. This step is a bit involved. - /// See the [rustc-dev-guide chapter] for more info. - /// - /// Then we map all the lifetimes in the concrete type to an equal - /// universal region that occurs in the opaque type's args, in this case - /// this would result in `&'a i32`. We only consider regions in the args - /// in case there is an equal region that does not. For example, this should - /// be allowed: - /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }` - /// - /// This will then allow `infer_opaque_definition_from_instantiation` to - /// determine that `_Return<'_x> = &'_x i32`. - /// - /// There's a slight complication around closures. Given - /// `fn f<'a: 'a>() { || {} }` the closure's type is something like - /// `f::<'a>::{{closure}}`. The region parameter from f is essentially - /// ignored by type checking so ends up being inferred to an empty region. - /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`, - /// which has no `external_name` in which case we use `'{erased}` as the - /// region to pass to `infer_opaque_definition_from_instantiation`. - /// - /// [rustc-dev-guide chapter]: - /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html - #[instrument(level = "debug", skip(self, root_cx, infcx), ret)] - pub(crate) fn infer_opaque_types( - &self, - root_cx: &mut BorrowCheckRootCtxt<'tcx>, - infcx: &InferCtxt<'tcx>, - opaque_ty_decls: FxIndexMap, 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", - ) - }); +#[allow(unused)] // TODO +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>), +} - 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; +impl<'tcx> BorrowCheckRootCtxt<'tcx> { + pub(crate) fn handle_opaque_type_uses(&mut self, borrowck_state: &mut BorrowckState<'tcx>) { + let BorrowckState { + infcx, + constraints, + universal_region_relations, + location_map, + deferred_opaque_type_errors, + .. + } = borrowck_state; + + handle_opaque_type_uses( + self, + infcx, + constraints, + deferred_opaque_type_errors, + universal_region_relations, + location_map, + ) + } +} + +struct RegionCtxt<'a, 'tcx> { + infcx: &'a BorrowckInferCtxt<'tcx>, + definitions: IndexVec>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + constraint_sccs: ConstraintSccs, + rev_scc_graph: ReverseSccGraph, + scc_values: RegionValues, +} + +impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { + fn new( + infcx: &'a BorrowckInferCtxt<'tcx>, + universal_region_relations: &'a Frozen>, + 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(); + + 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 = + constraints.outlives_constraints.compute_sccs(fr_static, &definitions); + let rev_scc_graph = ReverseSccGraph::compute(&constraint_sccs, universal_regions); + // Unlike the `RegionInferenceContext`, we only care about free regions + // and fully ignore liveness and placeholders. + let placeholder_indices = Default::default(); + let mut scc_values = + RegionValues::new(location_map, universal_regions.len(), placeholder_indices); + for variable in definitions.indices() { + let scc = constraint_sccs.scc(variable); + match definitions[variable].origin { + NllRegionVariableOrigin::FreeRegion => { + scc_values.add_element(scc, variable); } + _ => {} } + } + scc_values.propagate_constraints(&constraint_sccs); - 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), + RegionCtxt { + infcx, + definitions, + universal_region_relations, + constraint_sccs, + rev_scc_graph, + scc_values, + } + } + + fn representative(&self, vid: RegionVid) -> RegionVid { + let scc = self.constraint_sccs.scc(vid); + self.constraint_sccs.annotation(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>, + constraints: &mut MirTypeckRegionConstraints<'tcx>, + deferred_errors: &mut Vec>, + universal_region_relations: &Frozen>, + location_map: &Rc, +) { + let opaque_types = infcx.take_opaque_types(); + if opaque_types.is_empty() { + return; + } + + let tcx = infcx.tcx; + // We need to eagerly map all regions to NLL vars here, as we need to make sure we've + // introduced nll vars for all used placeholders. + let mut opaque_types = opaque_types + .into_iter() + .map(|entry| { + fold_regions(tcx, infcx.resolve_vars_if_possible(entry), |r, _| { + let vid = if let ty::RePlaceholder(placeholder) = r.kind() { + constraints.placeholder_region(infcx, placeholder).as_var() + } else { + universal_region_relations.universal_regions.to_region_vid(r) + }; + Region::new_var(tcx, vid) + }) + }) + .collect::>(); + + collect_defining_uses( + root_cx, + infcx, + constraints, + deferred_errors, + universal_region_relations, + location_map, + &mut opaque_types, + ); + + apply_defining_uses(root_cx, infcx, constraints, universal_region_relations, &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: &mut MirTypeckRegionConstraints<'tcx>, + deferred_errors: &mut Vec>, + universal_region_relations: &Frozen>, + location_map: &Rc, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], +) { + let tcx = infcx.tcx; + let 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 { + // Check whether the arguments are fully universal. + // + // FIXME: We currently treat `Opaque<'a, 'a>` as a defining use and then emit an error + // as it's not fully universal. We should share this code with `check_opaque_type_parameter_valid` + // to only consider actual defining uses as defining. + + let is_non_external_free_region = |r| { + let vid = rcx.representative(r); + matches!(rcx.definitions[vid].origin, NllRegionVariableOrigin::FreeRegion) + && !matches!( + rcx.universal_regions().region_classification(vid), + Some(RegionClassification::External) ) - .find(|(arg1, arg2)| arg1 != arg2) - { - infcx.dcx().emit_err(LifetimeMismatchOpaqueParam { - arg: arg1, - prev: arg2, - span: prev_span, - prev_span: concrete_type.span, - }); - } + }; + let arg_regions = opaque_type_key + .iter_captured_args(tcx) + .filter_map(|(_, arg)| arg.as_region()) + .map(Region::as_var) + .map(|r| if is_non_external_free_region(r) { Some(r) } else { None }); + let arg_regions = + iter::once(Some(rcx.universal_regions().fr_static)).chain(arg_regions).collect(); + + if let Some(arg_regions) = arg_regions { + defining_uses.push(DefiningUse { opaque_type_key, arg_regions, hidden_type }); } } - /// 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>, - { - fold_regions(tcx, ty, |region, _| match region.kind() { - ty::ReVar(vid) => { - let scc = self.constraint_sccs.scc(vid); + debug!(?defining_uses); - // 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); - } + apply_member_constraints(&rcx, constraints, &defining_uses); - // Fallback: this will produce a cryptic error message. - _ => return region, + // Applying member constraints may add new region constraints, so we + // need to recompute the region context before actually mapping the defining + // uses to the definition site. + // + // FIXME: We should be able to partially reuse and optimize this. + let rcx = + RegionCtxt::new(infcx, universal_region_relations, Rc::clone(location_map), constraints); + map_defining_uses_to_definition_site(root_cx, &rcx, &defining_uses, deferred_errors); +} + +fn apply_member_constraints<'tcx>( + rcx: &RegionCtxt<'_, 'tcx>, + constraints: &mut MirTypeckRegionConstraints<'tcx>, + defining_uses: &[DefiningUse<'tcx>], +) { + for DefiningUse { opaque_type_key: _, arg_regions, hidden_type } in defining_uses { + let mut visitor = + ApplyMemberConstraintsVisitor { rcx, arg_regions, span: hidden_type.span, constraints }; + hidden_type.ty.visit_with(&mut visitor); + } +} + +struct ApplyMemberConstraintsVisitor<'a, 'tcx> { + rcx: &'a RegionCtxt<'a, 'tcx>, + arg_regions: &'a [RegionVid], + span: Span, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, +} +impl<'tcx> ApplyMemberConstraintsVisitor<'_, '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 ApplyMemberConstraintsVisitor<'_, '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>) { + let member_vid = match r.kind() { + ty::ReBound(..) => return, + ty::ReVar(vid) => vid, + _ => unreachable!(), + }; + let member = self.rcx.constraint_sccs.scc(member_vid); + + // If the member region lives in a higher universe, we currently choose + // the most conservative option by leaving it unchanged. + if !self.rcx.constraint_sccs.annotation(member).min_universe().is_root() { + debug!("member not in root universe"); + return; + } + + // The existing value of `'member` is a lower-bound. If its is already larger than + // some universal region, we cannot equate it with that region. Said differently, we + // ignore choice regions which are smaller than this member region. + let mut choice_regions = self + .arg_regions + .iter() + .copied() + .map(|r| self.rcx.representative(r)) + .filter(|&choice_region| { + self.rcx.scc_values.universal_regions_outlived_by(member).all(|lower_bound| { + self.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 self.rcx.rev_scc_graph.upper_bounds(member) { + choice_regions.retain(|&choice_region| { + self.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| { + self.rcx.universal_region_relations.outlives(r1, r2) + || self.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 = self.rcx.universal_region_relations.outlives(r1, r2); + let r2_outlives_r1 = self.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 locations = Locations::All(self.span); + self.constraints.outlives_constraints.push(OutlivesConstraint { + sup: member_vid, + sub: min_choice, + locations, + span: self.span, + category: ConstraintCategory::OpaqueType, + variance_info: ty::VarianceDiagInfo::None, + from_closure: false, + }); + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return; + } + + 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() + }); + // 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 { + self.cx().erase_regions(arg) + } 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 { tcx.erase_regions(s) } else { s.fold_with(self) } + })); + ty::AliasTy::new_from_args(tcx, def_id, args).to_ty(tcx) + } + + _ => ty.super_fold_with(self), + } + } +} + +fn apply_defining_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + constraints: &mut MirTypeckRegionConstraints<'tcx>, + universal_region_relations: &Frozen>, + 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 universal_regions = &universal_region_relations.universal_regions; + let mut relation = + EquateRegions { infcx, span: hidden_type.span, universal_regions, constraints }; + match TypeRelation::relate(&mut relation, hidden_type.ty, expected.ty) { + Ok(_) => {} + Err(_) => { + let _ = hidden_type.build_mismatch_error(&expected, tcx).map(|d| d.emit()); + } + } + + // normalize -> equate + // + // Do we need to normalize? + // Only to support non-universal type or const args. It feels likely that we need (and want) to do so + // This once again needs to be careful about cycles: normalizing and equating while defining/revealing + // opaque types may end up introducing new defining uses. + + // How can we equate here? + // If we need to normalize the answer is just "it's TypeChecker time" + // without it we could manually walk over the types using a type relation and equate region vars + // that way. + } +} + +fn to_region_vid<'tcx>( + constraints: &MirTypeckRegionConstraints<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + r: Region<'tcx>, +) -> RegionVid { + if let ty::RePlaceholder(placeholder) = r.kind() { + constraints.get_placeholder_region(placeholder).as_var() + } else { + universal_regions.to_region_vid(r) + } +} + +// FUCKING SHIT NO!wegrdtfhergfsg +struct EquateRegions<'a, 'tcx> { + infcx: &'a BorrowckInferCtxt<'tcx>, + span: Span, + universal_regions: &'a UniversalRegions<'tcx>, + constraints: &'a mut MirTypeckRegionConstraints<'tcx>, +} + +impl<'tcx> TypeRelation> for EquateRegions<'_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn relate_with_variance>>( + &mut self, + _variance: ty::Variance, + _info: ty::VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.relate(a, b) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + structurally_relate_tys(self, a, b) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + if matches!(a.kind(), ty::ReBound(..)) || matches!(b.kind(), ty::ReBound(..)) { + assert_eq!(a, b); + return Ok(a); + } + + let a_vid = to_region_vid(self.constraints, self.universal_regions, a); + let b_vid = to_region_vid(self.constraints, self.universal_regions, b); + let locations = Locations::All(self.span); + self.constraints.outlives_constraints.push(OutlivesConstraint { + sup: a_vid, + sub: b_vid, + locations, + span: self.span, + category: ConstraintCategory::OpaqueType, + variance_info: ty::VarianceDiagInfo::None, + from_closure: false, + }); + self.constraints.outlives_constraints.push(OutlivesConstraint { + sup: b_vid, + sub: a_vid, + locations, + span: self.span, + category: ConstraintCategory::OpaqueType, + variance_info: ty::VarianceDiagInfo::None, + from_closure: false, + }); + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + structurally_relate_consts(self, a, b) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate>, + { + self.relate(a.skip_binder(), b.skip_binder())?; + Ok(a) } } @@ -262,19 +719,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(guar) = check_opaque_type_parameter_valid( + check_opaque_type_parameter_valid( self, opaque_type_key, instantiated_ty.span, DefiningScopeKind::MirBorrowck, - ) { - return Ty::new_error(self.tcx, guar); - } + )?; let definition_ty = instantiated_ty .remap_generic_params_to_declaration_params( @@ -284,10 +738,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.constraint_sccs.annotation(scc).min_universe().is_root() { + match self.scc_values.placeholders_contained_in(scc).enumerate().last() { + // If the region contains a single placeholder then they're equal. + Some((0, placeholder)) => { + return ty::Region::new_placeholder(tcx, placeholder); + } + + // Fallback: this will produce a cryptic error message. + _ => return region, + } + } + + // Find something that we can name + let upper_bound = self.approx_universal_upper_bound(vid); + if let Some(universal_region) = self.definitions[upper_bound].external_name { + return universal_region; + } - 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 b2ed8a3582796..a78209d6914ed 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -5,8 +5,9 @@ use rustc_data_structures::graph; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_middle::ty::RegionVid; -use crate::RegionInferenceContext; +use super::ConstraintSccs; use crate::constraints::ConstraintSccIndex; +use crate::universal_regions::UniversalRegions; pub(crate) struct ReverseSccGraph { graph: VecGraph, @@ -19,32 +20,14 @@ pub(crate) struct ReverseSccGraph { } impl ReverseSccGraph { - /// Find all universal regions that are required to outlive the given SCC. - pub(super) fn upper_bounds(&self, scc0: ConstraintSccIndex) -> impl Iterator { - let mut duplicates = FxIndexSet::default(); - graph::depth_first_search(&self.graph, scc0) - .flat_map(move |scc1| { - self.scc_regions - .get(&scc1) - .map_or(&[][..], |range| &self.universal_regions[range.clone()]) - }) - .copied() - .filter(move |r| duplicates.insert(*r)) - } -} - -impl RegionInferenceContext<'_> { - /// Compute the reverse SCC-based constraint graph (lazily). - pub(super) fn compute_reverse_scc_graph(&mut self) { - if self.rev_scc_graph.is_some() { - return; - } - - let graph = self.constraint_sccs.reverse(); - let mut paired_scc_regions = self - .universal_regions() + pub(super) fn compute( + constraint_sccs: &ConstraintSccs, + universal_regions: &UniversalRegions<'_>, + ) -> Self { + let graph = constraint_sccs.reverse(); + let mut paired_scc_regions = universal_regions .universal_regions_iter() - .map(|region| (self.constraint_sccs.scc(region), region)) + .map(|region| (constraint_sccs.scc(region), region)) .collect::>(); paired_scc_regions.sort(); let universal_regions = paired_scc_regions.iter().map(|&(_, region)| region).collect(); @@ -56,7 +39,19 @@ impl RegionInferenceContext<'_> { scc_regions.insert(scc, start..start + chunk.len()); start += chunk.len(); } + ReverseSccGraph { graph, scc_regions, universal_regions } + } - self.rev_scc_graph = Some(ReverseSccGraph { graph, scc_regions, universal_regions }); + /// Find all universal regions that are required to outlive the given SCC. + pub(super) fn upper_bounds(&self, scc0: ConstraintSccIndex) -> impl Iterator { + let mut duplicates = FxIndexSet::default(); + graph::depth_first_search(&self.graph, scc0) + .flat_map(move |scc1| { + self.scc_regions + .get(&scc1) + .map_or(&[][..], |range| &self.universal_regions[range.clone()]) + }) + .copied() + .filter(move |r| duplicates.insert(*r)) } } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index d9ac5b5cb132a..f01a8c6875789 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -8,9 +8,11 @@ use rustc_index::interval::{IntervalSet, SparseIntervalMatrix}; use rustc_middle::mir::{BasicBlock, Location}; use rustc_middle::ty::{self, RegionVid}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; -use tracing::debug; +use tracing::{debug, instrument}; +use super::ConstraintSccs; use crate::BorrowIndex; +use crate::constraints::ConstraintSccIndex; use crate::polonius::LiveLoans; rustc_index::newtype_index! { @@ -37,6 +39,7 @@ pub(crate) enum RegionElement { /// Records the CFG locations where each region is live. When we initially compute liveness, we use /// an interval matrix storing liveness ranges for each region-vid. +#[derive(Clone)] pub(crate) struct LivenessValues { /// The map from locations to points. location_map: Rc, @@ -193,7 +196,7 @@ impl LivenessValues { /// Maps from `ty::PlaceholderRegion` values that are used in the rest of /// rustc to the internal `PlaceholderIndex` values that are used in /// NLL. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub(crate) struct PlaceholderIndices { indices: FxIndexSet, } @@ -424,6 +427,40 @@ impl ToElementIndex for ty::PlaceholderRegion { } } +impl RegionValues { + /// Propagate the region constraints: this will grow the values + /// for each region variable until all the constraints are + /// satisfied. Note that some values may grow **too** large to be + /// feasible, but we check this later. + #[instrument(skip(self, constraint_sccs), level = "debug")] + pub(super) fn propagate_constraints(&mut self, constraint_sccs: &ConstraintSccs) { + // To propagate constraints, we walk the DAG induced by the + // SCC. For each SCC, we visit its successors and compute + // their values, then we union all those values to get our + // own. + for scc in constraint_sccs.all_sccs() { + self.compute_value_for_scc(constraint_sccs, scc); + } + } + + /// Computes the value of the SCC `scc_a`, which has not yet been + /// computed, by unioning the values of its successors. + /// Assumes that all successors have been computed already + /// (which is assured by iterating over SCCs in dependency order). + #[instrument(skip(self, constraint_sccs), level = "debug")] + fn compute_value_for_scc( + &mut self, + constraint_sccs: &ConstraintSccs, + scc_a: ConstraintSccIndex, + ) { + // Walk each SCC `B` such that `A: B`... + for &scc_b in constraint_sccs.successors(scc_a) { + debug!(?scc_b); + self.add_region(scc_a, scc_b); + } + } +} + /// For debugging purposes, returns a pretty-printed string of the given points. pub(crate) fn pretty_print_points( location_map: &DenseLocationMap, diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 13daa5c722148..af7c4cd107705 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,44 +73,132 @@ 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); } - fn get_or_insert_nested(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> { + fn compute_partial_results(&mut self, def_id: LocalDefId) { debug_assert_eq!( self.tcx.typeck_root_def_id(def_id.to_def_id()), self.root_def_id.to_def_id() ); - if !self.nested_bodies.contains_key(&def_id) { - let result = super::do_mir_borrowck(self, def_id, None).0; - if let Some(prev) = self.nested_bodies.insert(def_id, result) { + if !self.partial_results.contains_key(&def_id) { + let result = start_do_mir_borrowck(self, def_id, None); + // We only need to store the partial result if it depends on opaque types + // or depends on a nested item which does. + let relies_on_opaques = result.infcx.has_opaque_types_in_storage() + || !result.deferred_closure_requirements.is_empty(); + + let to_insert = if !relies_on_opaques { + let final_result = resume_do_mir_borrowck(self, None, result).0; + if self.final_results.insert(def_id, final_result).is_some() { + bug!("unexpected previous final result for {def_id:?}"); + } + None + } else { + Some(result) + }; + + if self.partial_results.insert(def_id, to_insert).is_some() { + bug!("unexpected previous partial result for: {def_id:?}") + } + } + } + + fn get_or_insert_final(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> { + debug_assert_eq!( + self.tcx.typeck_root_def_id(def_id.to_def_id()), + self.root_def_id.to_def_id() + ); + if !self.final_results.contains_key(&def_id) { + let mut yield_do_mir_borrowck = self.partial_results.remove(&def_id).unwrap().unwrap(); + self.handle_opaque_type_uses(&mut yield_do_mir_borrowck); + apply_closure_requirements_considering_opaques(self, &mut yield_do_mir_borrowck); + let result = resume_do_mir_borrowck(self, None, yield_do_mir_borrowck).0; + if let Some(prev) = self.final_results.insert(def_id, result) { bug!("unexpected previous nested body: {prev:?}"); } } - self.nested_bodies.get(&def_id).unwrap() + self.final_results.get(&def_id).unwrap() } - pub(super) fn closure_requirements( + pub(crate) fn get_closure_requirements_considering_regions( &mut self, - nested_body_def_id: LocalDefId, + def_id: LocalDefId, ) -> &Option> { - &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); + #[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/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 536a27763d29c..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>, @@ -41,8 +41,8 @@ type NormalizedInputsAndOutput<'tcx> = Vec>; pub(crate) struct CreateResult<'tcx> { pub(crate) universal_region_relations: Frozen>, - pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>, - pub(crate) known_type_outlives_obligations: Vec>, + pub(crate) region_bound_pairs: Frozen>, + pub(crate) known_type_outlives_obligations: Frozen>>, pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'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 { @@ -333,8 +326,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { outlives: self.outlives.freeze(), inverse_outlives: self.inverse_outlives.freeze(), }), - known_type_outlives_obligations, - region_bound_pairs: self.region_bound_pairs, + known_type_outlives_obligations: Frozen::freeze(known_type_outlives_obligations), + region_bound_pairs: Frozen::freeze(self.region_bound_pairs), normalized_inputs_and_output, } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 3c00b819a96bb..8a0cf827fbe9b 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)*) => ({ @@ -74,7 +75,6 @@ mod constraint_conversion; pub(crate) mod free_region_relations; mod input_output; pub(crate) mod liveness; -mod opaque_types; mod relate_tys; /// Type checks the given `mir` in the context of the inference @@ -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(), }; @@ -151,14 +150,15 @@ pub(crate) fn type_check<'a, 'tcx>( body, promoted, user_type_annotations: &body.user_type_annotations, - region_bound_pairs, - known_type_outlives_obligations, + region_bound_pairs: ®ion_bound_pairs, + known_type_outlives_obligations: &known_type_outlives_obligations, reported_errors: Default::default(), universal_regions: &universal_region_relations.universal_regions, location_table, polonius_facts, borrow_set, constraints: &mut constraints, + deferred_closure_requirements: Default::default(), polonius_liveness, }; @@ -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, } } @@ -216,14 +219,15 @@ struct TypeChecker<'a, 'tcx> { /// User type annotations are shared between the main MIR and the MIR of /// all of the promoted items. user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, - region_bound_pairs: RegionBoundPairs<'tcx>, - known_type_outlives_obligations: Vec>, + region_bound_pairs: &'a RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &'a Vec>, reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, universal_regions: &'a UniversalRegions<'tcx>, location_table: &'a PoloniusLocationTable, 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, @@ -291,6 +296,16 @@ impl<'tcx> MirTypeckRegionConstraints<'tcx> { } } } + + /// Same as `Self::placeholder_region`, except that never adds new regions but instead ICEs in case we + /// haven't already created an NLL var for this placeholders. + pub(crate) fn get_placeholder_region( + &self, + placeholder: ty::PlaceholderRegion, + ) -> ty::Region<'tcx> { + let placeholder_index = self.placeholder_indices.lookup_index(placeholder); + *self.placeholder_index_to_region.get(placeholder_index).unwrap() + } } /// The `Locations` type summarizes *where* region constraints are @@ -368,14 +383,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.body } - fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::RePlaceholder(placeholder) = r.kind() { - self.constraints.placeholder_region(self.infcx, placeholder).as_var() - } else { - self.universal_regions.to_region_vid(r) - } - } - fn unsized_feature_enabled(&self) -> bool { let features = self.tcx().features(); features.unsized_locals() || features.unsized_fn_params() @@ -2496,7 +2503,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, @@ -2550,6 +2563,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 d41cbf757d7f8..0000000000000 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ /dev/null @@ -1,337 +0,0 @@ -use std::iter; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_middle::span_bug; -use rustc_middle::ty::{ - self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions, -}; -use tracing::{debug, trace}; - -use super::{MemberConstraintSet, TypeChecker}; - -/// Once we're done with typechecking the body, we take all the opaque types -/// defined by this function and add their 'member constraints'. -pub(super) fn take_opaques_and_register_member_constraints<'tcx>( - typeck: &mut TypeChecker<'_, 'tcx>, -) -> FxIndexMap, 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_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { - t.super_visit_with(self); - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) { - match r.kind() { - // ignore bound regions, keep visiting - ty::ReBound(_, _) => {} - _ => (self.op)(r), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) { - // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { - return; - } - - match *ty.kind() { - ty::Closure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_closure().upvar_tys() { - upvar.visit_with(self); - } - args.as_closure().sig_as_fn_ptr_ty().visit_with(self); - } - - ty::CoroutineClosure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_coroutine_closure().upvar_tys() { - upvar.visit_with(self); - } - - args.as_coroutine_closure().signature_parts_ty().visit_with(self); - } - - ty::Coroutine(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - // Also skip the witness type, because that has no free regions. - - for upvar in args.as_coroutine().upvar_tys() { - upvar.visit_with(self); - } - args.as_coroutine().return_ty().visit_with(self); - args.as_coroutine().yield_ty().visit_with(self); - args.as_coroutine().resume_ty().visit_with(self); - } - - ty::Alias(kind, ty::AliasTy { def_id, args, .. }) - if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) => - { - // Skip lifetime parameters that are not captured, since they do - // not need member constraints registered for them; we'll erase - // them (and hopefully in the future replace them with placeholders). - for (v, s) in std::iter::zip(variances, args.iter()) { - if *v != ty::Bivariant { - s.visit_with(self); - } - } - } - - _ => { - ty.super_visit_with(self); - } - } - } -} 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_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index bf91eb1b8fdac..4419d5dc7d663 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -9,8 +9,8 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{AmbigArg, HirId}; use rustc_middle::bug; use rustc_middle::ty::{ - self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, Upcast, + self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, }; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::traits; @@ -996,7 +996,7 @@ struct GenericParamAndBoundVarCollector<'a, 'tcx> { impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 'tcx> { type Result = ControlFlow; - fn visit_binder>>( + fn visit_binder>>( &mut self, binder: &ty::Binder<'tcx, T>, ) -> Self::Result { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 9c6d4ee096f1a..ca62ca81d5993 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1589,7 +1589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let rcvr_t = self.check_expr(rcvr); // no need to check for bot/err -- callee does that - let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t); + let rcvr_t = self.try_structurally_resolve_type(rcvr.span, rcvr_t); let method = match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) { diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index a614b4f00ffe4..e5d689e584629 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -190,7 +190,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { assert_eq!(n, pick.autoderefs); let mut adjustments = self.adjust_steps(&autoderef); - let mut target = self.structurally_resolve_type(autoderef.span(), ty); + let mut target = self.try_structurally_resolve_type(autoderef.span(), ty); match pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 1d3a081cbb884..45eaa1df98198 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -10,15 +10,16 @@ use rustc_hir::HirId; use rustc_hir::def::DefKind; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; -use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCauseCode; +use rustc_infer::infer::{self, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::traits::solve::Goal; +use rustc_infer::traits::{ObligationCauseCode, PredicateObligation}; use rustc_middle::middle::stability; use rustc_middle::query::Providers; use rustc_middle::ty::elaborate::supertrait_def_ids; use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; use rustc_middle::ty::{ self, AssocItem, AssocItemContainer, GenericArgs, GenericArgsRef, GenericParamDefKind, - ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, Upcast, + ParamEnvAnd, PredicateKind, Ty, TyCtxt, TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -29,6 +30,9 @@ use rustc_span::edit_distance::{ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::solve::inspect::{ + InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor, +}; use rustc_trait_selection::traits::query::CanonicalTyGoal; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::{ @@ -442,7 +446,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we encountered an `_` type or an error type during autoderef, this is // ambiguous. if let Some(bad_ty) = &steps.opt_bad_ty { - if is_suggestion.0 { + // Ended up encountering a type variable when doing autoderef, + // but it may not be a type variable after processing obligations + // in our local `FnCtxt`, so don't call `structurally_resolve_type`. + let ty = &bad_ty.ty; + let ty = self + .probe_instantiate_query_response(span, &orig_values, ty) + .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); + if bad_ty.is_opaque_type + || final_ty_is_opaque( + &self.infcx, + &self.fulfillment_cx.borrow().pending_obligations(), + ty.value, + ) + { + // FIXME(-Znext-solver): This isn't really what we want :< + assert!(self.tcx.next_trait_solver_globally()); + } else if is_suggestion.0 { // Ambiguity was encountered during a suggestion. There's really // not much use in suggesting methods in this case. return Err(MethodError::NoMatch(NoMatchData { @@ -468,13 +488,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); } else { - // Ended up encountering a type variable when doing autoderef, - // but it may not be a type variable after processing obligations - // in our local `FnCtxt`, so don't call `structurally_resolve_type`. - let ty = &bad_ty.ty; - let ty = self - .probe_instantiate_query_response(span, &orig_values, ty) - .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { ty::Infer(ty::TyVar(_)) => { @@ -582,60 +595,77 @@ fn method_autoderef_steps<'tcx>( let mut reached_raw_pointer = false; let arbitrary_self_types_enabled = tcx.features().arbitrary_self_types() || tcx.features().arbitrary_self_types_pointers(); - let (mut steps, reached_recursion_limit): (Vec<_>, bool) = if arbitrary_self_types_enabled { - let reachable_via_deref = - autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false)); - - let mut autoderef_via_receiver = - Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) - .include_raw_pointers() - .use_receiver_trait() - .silence_errors(); - let steps = autoderef_via_receiver - .by_ref() - .zip(reachable_via_deref) - .map(|((ty, d), reachable_via_deref)| { - let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, ty), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - reachable_via_deref, - }; - if ty.is_raw_ptr() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - (steps, autoderef_via_receiver.reached_recursion_limit()) - } else { - let steps = autoderef_via_deref - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, ty), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - reachable_via_deref: true, - }; - if ty.is_raw_ptr() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - (steps, autoderef_via_deref.reached_recursion_limit()) - }; - let final_ty = autoderef_via_deref.final_ty(true); + let (mut steps, final_ty, reached_recursion_limit, obligations): (Vec<_>, _, _, _) = + if arbitrary_self_types_enabled { + let reachable_via_deref = + autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false)); + + let mut autoderef_via_receiver = + Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) + .include_raw_pointers() + .use_receiver_trait() + .silence_errors(); + let steps = autoderef_via_receiver + .by_ref() + .zip(reachable_via_deref) + .map(|((ty, d), reachable_via_deref)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars, ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + reachable_via_deref, + }; + if ty.is_raw_ptr() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; + } + step + }) + .collect(); + ( + steps, + // FIXME(arbitrary_self_types): This is sus. + autoderef_via_deref.final_ty(true), + autoderef_via_receiver.reached_recursion_limit(), + autoderef_via_receiver.into_obligations(), + ) + } else { + let steps = autoderef_via_deref + .by_ref() + .map(|(ty, d)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars, ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + reachable_via_deref: true, + }; + if ty.is_raw_ptr() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; + } + step + }) + .collect(); + ( + steps, + autoderef_via_deref.final_ty(true), + autoderef_via_deref.reached_recursion_limit(), + autoderef_via_deref.into_obligations(), + ) + }; let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + ty::Infer(ty::TyVar(_)) => Some(MethodAutoderefBadTy { reached_raw_pointer, + is_opaque_type: final_ty_is_opaque(infcx, &obligations, final_ty), + ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Error(_) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + is_opaque_type: false, ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), }), ty::Array(elem_ty, _) => { @@ -668,6 +698,71 @@ fn method_autoderef_steps<'tcx>( } } +/// Returns `true` in case the final type is the hidden type of an opaque. +#[instrument(level = "debug", skip(infcx), ret)] +fn final_ty_is_opaque<'tcx>( + infcx: &InferCtxt<'tcx>, + obligations: &[PredicateObligation<'tcx>], + final_ty: Ty<'tcx>, +) -> bool { + // nyaaaa~ + if infcx.next_trait_solver() { + for obligation in obligations { + let mut visitor = FinalTyIsOpaque { final_ty, is_opaque_ty: false }; + let goal = Goal::new(infcx.tcx, obligation.param_env, obligation.predicate); + infcx.visit_proof_tree(goal, &mut visitor); + if visitor.is_opaque_ty { + return true; + } + } + + let opaque_types = infcx.clone_opaque_types(); + for (_, hidden_ty) in &opaque_types { + if infcx.shallow_resolve(hidden_ty.ty) == final_ty { + return true; + } + } + } + + false +} + +struct FinalTyIsOpaque<'tcx> { + final_ty: Ty<'tcx>, + is_opaque_ty: bool, +} + +impl<'tcx> ProofTreeVisitor<'tcx> for FinalTyIsOpaque<'tcx> { + fn span(&self) -> Span { + DUMMY_SP + } + + fn config(&self) -> InspectConfig { + // Using an intentionally low depth to avoid potential hangs + // due to exponentially growing proof trees. + InspectConfig { max_depth: 5 } + } + + fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { + let infcx = inspect_goal.infcx(); + let goal = inspect_goal.goal(); + if let PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) = + goal.predicate.kind().skip_binder() + { + debug!(?alias, ?term, "visiting normalizes-to goal"); + if term.as_type().is_some_and(|ty| ty == self.final_ty) + && alias.kind(infcx.tcx) == ty::AliasTermKind::OpaqueTy + { + self.is_opaque_ty = true; + } + } + + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_in_probe(self) + } + } +} + impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn new( fcx: &'a FnCtxt<'a, 'tcx>, @@ -1881,31 +1976,39 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); - match self_ty.kind() { - // HACK: opaque types will match anything for which their bounds hold. - // Thus we need to prevent them from trying to match the `&_` autoref - // candidates that get created for `&self` trait methods. - ty::Alias(ty::Opaque, alias_ty) - if !self.next_trait_solver() - && self.infcx.can_define_opaque_ty(alias_ty.def_id) - && !xform_self_ty.is_ty_var() => - { - return ProbeResult::NoMatch; - } - _ => match ocx.relate( - cause, - self.param_env, - self.variance(), - self_ty, - xform_self_ty, - ) { - Ok(()) => {} - Err(err) => { - debug!("--> cannot relate self-types {:?}", err); + + // HACK: opaque types will match anything for which their bounds hold. + // Thus we need to prevent them from trying to match the `&_` autoref + // candidates that get created for `&self` trait methods. + if self.mode == Mode::MethodCall { + match self_ty.kind() { + ty::Infer(ty::TyVar(_)) => { + assert!(self.infcx.next_trait_solver()); + if !xform_self_ty.is_ty_var() { + return ProbeResult::NoMatch; + } + } + ty::Alias(ty::Opaque, alias_ty) + if !self.infcx.next_trait_solver() + && self.infcx.can_define_opaque_ty(alias_ty.def_id) + && !xform_self_ty.is_ty_var() => + { + assert!(!self.infcx.next_trait_solver()); return ProbeResult::NoMatch; } - }, + _ => {} + } + } + + match ocx.relate(cause, self.param_env, self.variance(), self_ty, xform_self_ty) + { + Ok(()) => {} + Err(err) => { + debug!("--> cannot relate self-types {:?}", err); + return ProbeResult::NoMatch; + } } + let obligation = traits::Obligation::new( self.tcx, cause.clone(), diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 3caaa7f9a9d0b..880f484edae81 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -581,15 +581,16 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } - if let Err(guar) = check_opaque_type_parameter_valid( + if let Err(err) = check_opaque_type_parameter_valid( &self.fcx, opaque_type_key, hidden_type.span, DefiningScopeKind::HirTypeck, ) { - self.typeck_results - .concrete_opaque_types - .insert(opaque_type_key.def_id, ty::OpaqueHiddenType::new_error(tcx, guar)); + self.typeck_results.concrete_opaque_types.insert( + opaque_type_key.def_id, + ty::OpaqueHiddenType::new_error(tcx, err.report(self.fcx)), + ); } let hidden_type = hidden_type.remap_generic_params_to_declaration_params( diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c4698e5cbb456..72a5880be444b 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -953,6 +953,10 @@ impl<'tcx> InferCtxt<'tcx> { storage.var_infos.clone() } + pub fn has_opaque_types_in_storage(&self) -> bool { + !self.inner.borrow().opaque_type_storage.opaque_types.is_empty() + } + #[instrument(level = "debug", skip(self), ret)] pub fn take_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> { std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index c44d9723f29d7..2a4b9776f68cb 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -24,10 +24,6 @@ impl<'tcx, OP> TypeVisitor> for FreeRegionsVisitor<'tcx, OP> where OP: FnMut(ty::Region<'tcx>), { - fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { - t.super_visit_with(self); - } - fn visit_region(&mut self, r: ty::Region<'tcx>) { match r.kind() { // ignore bound regions, keep visiting diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index f1dc420aa3c92..30331b82bb7d8 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -15,7 +15,8 @@ use rustc_middle::ty::relate::{ Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys, }; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint::FutureIncompatibilityReason; @@ -209,7 +210,7 @@ where VarFn: FnOnce() -> FxHashMap, OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>, { - fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { + fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { // When we get into a binder, we need to add its own bound vars to the scope. let mut added = vec![]; for arg in t.bound_vars() { diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 76f3d2bab9cf4..31031ac25a128 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -178,6 +178,7 @@ pub struct MethodAutoderefStepsResult<'tcx> { #[derive(Debug, HashStable)] pub struct MethodAutoderefBadTy<'tcx> { pub reached_raw_pointer: bool, + pub is_opaque_type: bool, pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 98057a25f04c7..0f5b6d22ca6f2 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3298,11 +3298,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/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 02e316dfc3db7..551d816941b6e 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -121,11 +121,10 @@ impl<'tcx> Predicate<'tcx> { /// unsoundly accept some programs. See #91068. #[inline] pub fn allow_normalization(self) -> bool { - // Keep this in sync with the one in `rustc_type_ir::inherent`! match self.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) - | PredicateKind::AliasRelate(..) - | PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { + false + } PredicateKind::Clause(ClauseKind::Trait(_)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) @@ -137,6 +136,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Coerce(_) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) | PredicateKind::ConstEquate(_, _) + | PredicateKind::NormalizesTo(..) | PredicateKind::Ambiguous => true, } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index d739218af5ee7..9660862c35483 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2934,7 +2934,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { fn prepare_region_info(&mut self, value: &ty::Binder<'tcx, T>) where - T: TypeVisitable>, + T: TypeFoldable>, { struct RegionNameCollector<'tcx> { used_region_names: FxHashSet, diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 44c7b6a7c9e8d..1d92b8fd0970d 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -66,7 +66,7 @@ impl<'tcx> TyCtxt<'tcx> { { type Result = ControlFlow<()>; - fn visit_binder>>( + fn visit_binder>>( &mut self, t: &Binder<'tcx, T>, ) -> Self::Result { @@ -168,7 +168,7 @@ impl LateBoundRegionsCollector { } impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { - fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) { + fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) { self.current_index.shift_in(1); t.super_visit_with(self); self.current_index.shift_out(1); diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 63aa60f2f26b9..36d53901d9e82 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -9,7 +9,6 @@ derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } tracing = "0.1" @@ -20,7 +19,6 @@ default = ["nightly"] nightly = [ "dep:rustc_data_structures", "dep:rustc_macros", - "dep:rustc_serialize", "rustc_index/nightly", "rustc_type_ir/nightly", ] diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index 0fc313e33b323..344098417e32c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -15,12 +15,21 @@ //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. +use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; -use rustc_type_ir::{self as ty, Interner}; +use rustc_type_ir::solve::GoalSource; +use rustc_type_ir::{ + self as ty, Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, +}; use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; +use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult}; + +enum IgnoreAliases { + Yes, + No, +} impl EvalCtxt<'_, D> where @@ -46,10 +55,19 @@ where || rhs.is_error() ); + if self.alias_cannot_name_placeholder_in_rigid(param_env, lhs, rhs) + || self.alias_cannot_name_placeholder_in_rigid(param_env, rhs, lhs) + { + return Err(NoSolution); + } + // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { let term = self.next_term_infer_of_kind(lhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { lhs @@ -58,7 +76,10 @@ where // Structurally normalize the rhs. let rhs = if let Some(alias) = rhs.to_alias_term() { let term = self.next_term_infer_of_kind(rhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { rhs @@ -106,4 +127,132 @@ where } } } + + /// In case a rigid term refers to a placeholder which is not referenced by the + /// alias, the alias cannot be normalized to that rigid term unless it contains + /// either inference variables or these placeholders are referenced in a term + /// of a `Projection`-clause in the environment. + fn alias_cannot_name_placeholder_in_rigid( + &mut self, + param_env: I::ParamEnv, + rigid_term: I::Term, + alias: I::Term, + ) -> bool { + // Check that the rigid term is actually rigid. + if rigid_term.to_alias_term().is_some() || alias.to_alias_term().is_none() { + return false; + } + + // If the alias has any type or const inference variables, + // do not try to apply the fast path as these inference variables + // may resolve to something containing placeholders. + if alias.has_non_region_infer() { + return false; + } + + let mut referenced_placeholders = Default::default(); + self.collect_placeholders_in_term( + rigid_term, + IgnoreAliases::Yes, + &mut referenced_placeholders, + ); + if referenced_placeholders.is_empty() { + return false; + } + + let mut alias_placeholders = Default::default(); + self.collect_placeholders_in_term(alias, IgnoreAliases::No, &mut alias_placeholders); + loop { + let mut has_changed = false; + for clause in param_env.caller_bounds().iter() { + match clause.kind().skip_binder() { + ty::ClauseKind::Projection(ty::ProjectionPredicate { + projection_term, + term, + }) => { + let mut required_placeholders = Default::default(); + for term in projection_term.args.iter().filter_map(|arg| arg.as_term()) { + self.collect_placeholders_in_term( + term, + IgnoreAliases::Yes, + &mut required_placeholders, + ); + } + + if !required_placeholders.is_subset(&alias_placeholders) { + continue; + } + + if term.has_non_region_infer() { + return false; + } + + has_changed |= self.collect_placeholders_in_term( + term, + IgnoreAliases::No, + &mut alias_placeholders, + ); + } + ty::ClauseKind::Trait(_) + | ty::ClauseKind::HostEffect(_) + | ty::ClauseKind::TypeOutlives(_) + | ty::ClauseKind::RegionOutlives(_) + | ty::ClauseKind::ConstArgHasType(..) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) => continue, + } + } + + if !has_changed { + break; + } + } + // If the rigid term references a placeholder not mentioned by the alias, + // they can never unify. + !referenced_placeholders.is_subset(&alias_placeholders) + } + + fn collect_placeholders_in_term( + &mut self, + term: I::Term, + ignore_aliases: IgnoreAliases, + placeholders: &mut HashSet, + ) -> bool { + // Fast path to avoid walking the term. + if !term.has_placeholders() { + return false; + } + + struct PlaceholderCollector<'a, I: Interner> { + ignore_aliases: IgnoreAliases, + has_changed: bool, + placeholders: &'a mut HashSet, + } + impl TypeVisitor for PlaceholderCollector<'_, I> { + type Result = (); + + fn visit_ty(&mut self, t: I::Ty) { + match t.kind() { + ty::Placeholder(_) => self.has_changed |= self.placeholders.insert(t.into()), + ty::Alias(..) if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {} + _ => t.super_visit_with(self), + } + } + + fn visit_const(&mut self, ct: I::Const) { + match ct.kind() { + ty::ConstKind::Placeholder(_) => { + self.has_changed |= self.placeholders.insert(ct.into()) + } + ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) + if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {} + _ => ct.super_visit_with(self), + } + } + } + + let mut visitor = PlaceholderCollector { ignore_aliases, has_changed: false, placeholders }; + term.visit_with(&mut visitor); + visitor.has_changed + } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index ecb57cc0ad71b..15e4b4281347d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,16 +2,21 @@ pub(super) mod structural_traits; +use std::ops::ControlFlow; + use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::{ - self as ty, Interner, TypeFoldable, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate, + self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, + TypeVisitor, TypingMode, Upcast as _, elaborate, }; use tracing::{debug, instrument}; +use super::inspect; use super::trait_goals::TraitGoalProvenVia; use crate::delegate::SolverDelegate; +use crate::resolve::EagerResolver; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, @@ -118,6 +123,38 @@ where alias_ty: ty::AliasTy, ) -> Vec>; + fn probe_and_consider_param_env_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + idx: usize, + ) -> Result, NoSolution> { + ecx.probe(|candidate: &Result, NoSolution>| match candidate { + Ok(candidate) => inspect::ProbeKind::TraitCandidate { + source: candidate.source, + result: Ok(candidate.result), + }, + Err(NoSolution) => inspect::ProbeKind::TraitCandidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result: Err(NoSolution), + }, + }) + .enter(|ecx| { + Self::match_param_env_candidate(ecx, goal, assumption)?; + let source = ecx.characterize(goal.param_env, assumption, idx)?; + Ok(Candidate { + source, + result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?, + }) + }) + } + + fn match_param_env_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution>; + fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -508,13 +545,7 @@ where candidates: &mut Vec>, ) { for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { - candidates.extend(G::probe_and_consider_implied_clause( - self, - CandidateSource::ParamEnv(i), - goal, - assumption, - [], - )); + candidates.extend(G::probe_and_consider_param_env_candidate(self, goal, assumption, i)); } } @@ -831,11 +862,20 @@ where // See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`. let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds .iter() - .any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) - { + .any(|c| { + matches!( + c.source, + CandidateSource::ParamEnv(_) | CandidateSource::GlobalParamEnv(_) + ) + }) { candidates_from_env_and_bounds .into_iter() - .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .filter(|c| { + matches!( + c.source, + CandidateSource::ParamEnv(_) | CandidateSource::GlobalParamEnv(_) + ) + }) .map(|c| c.result) .collect() } else { @@ -864,7 +904,12 @@ where // (for example, and ideally only) when proving item bounds for an impl. let candidates_from_env: Vec<_> = candidates .iter() - .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .filter(|c| { + matches!( + c.source, + CandidateSource::ParamEnv(_) | CandidateSource::GlobalParamEnv(_) + ) + }) .map(|c| c.result) .collect(); if let Some(response) = self.try_merge_responses(&candidates_from_env) { @@ -880,4 +925,77 @@ where } } } + + fn characterize( + &mut self, + param_env: I::ParamEnv, + assumption: I::Clause, + idx: usize, + ) -> Result, NoSolution> { + // FIXME: + if assumption.has_bound_vars() { + return Ok(CandidateSource::ParamEnv(idx)); + } + + match assumption.visit_with(&mut FindParamInClause { ecx: self, param_env }) { + ControlFlow::Break(Err(NoSolution)) => Err(NoSolution), + ControlFlow::Break(Ok(())) => Ok(CandidateSource::ParamEnv(idx)), + ControlFlow::Continue(()) => Ok(CandidateSource::GlobalParamEnv(idx)), + } + } +} + +struct FindParamInClause<'a, 'b, D: SolverDelegate, I: Interner> { + ecx: &'a mut EvalCtxt<'b, D>, + param_env: I::ParamEnv, +} + +impl TypeVisitor for FindParamInClause<'_, '_, D, I> +where + D: SolverDelegate, + I: Interner, +{ + type Result = ControlFlow>; + + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + self.ecx.enter_forall(t.clone(), |ecx, v| { + v.visit_with(&mut FindParamInClause { ecx, param_env: self.param_env }) + }) + } + + fn visit_ty(&mut self, ty: I::Ty) -> Self::Result { + let Ok(ty) = self.ecx.structurally_normalize_ty(self.param_env, ty) else { + return ControlFlow::Break(Err(NoSolution)); + }; + let ty = ty.fold_with(&mut EagerResolver::new(self.ecx.delegate)); + + if let ty::Placeholder(_) = ty.kind() { + ControlFlow::Break(Ok(())) + } else { + ty.super_visit_with(self) + } + } + + fn visit_const(&mut self, ct: I::Const) -> Self::Result { + let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else { + return ControlFlow::Break(Err(NoSolution)); + }; + let ct = ct.fold_with(&mut EagerResolver::new(self.ecx.delegate)); + + if let ty::ConstKind::Placeholder(_) = ct.kind() { + ControlFlow::Break(Ok(())) + } else { + ct.super_visit_with(self) + } + } + + fn visit_region(&mut self, r: I::Region) -> Self::Result { + match r.kind() { + ty::ReStatic | ty::ReError(_) => ControlFlow::Continue(()), + ty::ReVar(_) | ty::RePlaceholder(_) => ControlFlow::Break(Ok(())), + ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReBound(..) => { + unreachable!() + } + } + } } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 7752a705cd145..c93d74d82e6bd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -40,7 +40,7 @@ where ecx: &mut EvalCtxt<'_, D>, source: rustc_type_ir::solve::CandidateSource, goal: Goal, - assumption: ::Clause, + assumption: I::Clause, then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, ) -> Result, NoSolution> { if let Some(host_clause) = assumption.as_host_effect_clause() { @@ -71,6 +71,32 @@ where } } + fn match_param_env_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution> { + if let Some(host_clause) = assumption.as_host_effect_clause() { + if host_clause.def_id() == goal.predicate.def_id() + && host_clause.constness().satisfies(goal.predicate.constness) + { + if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + host_clause.skip_binder().trait_ref.args, + ) { + return Err(NoSolution); + } + + let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause); + ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref) + } else { + Err(NoSolution) + } + } else { + Err(NoSolution) + } + } + /// Register additional assumptions for aliases corresponding to `~const` item bounds. /// /// Unlike item bounds, they are not simply implied by the well-formedness of the alias. @@ -124,7 +150,7 @@ where fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - impl_def_id: ::DefId, + impl_def_id: I::DefId, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -178,7 +204,7 @@ where fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, D>, - _guar: ::ErrorGuaranteed, + _guar: I::ErrorGuaranteed, ) -> Result, NoSolution> { ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index d56b0e5847e57..04f80a056f94d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -22,7 +22,7 @@ use tracing::{debug, instrument, trace}; use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::EagerResolver; -use crate::solve::eval_ctxt::{CurrentGoalKind, NestedGoals}; +use crate::solve::eval_ctxt::CurrentGoalKind; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, @@ -112,13 +112,9 @@ where // by `try_evaluate_added_goals()`. let (certainty, normalization_nested_goals) = match self.current_goal_kind { CurrentGoalKind::NormalizesTo => { - let NestedGoals { normalizes_to_goals, goals } = - std::mem::take(&mut self.nested_goals); - if cfg!(debug_assertions) { - assert!(normalizes_to_goals.is_empty()); - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); - } + let goals = std::mem::take(&mut self.nested_goals); + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); } (certainty, NestedNormalizationGoals(goals)) } 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 9994c85d0d0d4..e03ff2f26185f 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 @@ -1,8 +1,8 @@ +use std::mem; use std::ops::ControlFlow; -use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +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::*; @@ -14,7 +14,6 @@ use rustc_type_ir::{ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::{instrument, trace}; use crate::coherence; @@ -88,7 +87,7 @@ where /// If some `InferCtxt` method is missing, please first think defensively about /// the method's compatibility with this solver, or if an existing one does /// the job already. - delegate: &'a D, + pub delegate: &'a D, /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. @@ -114,7 +113,7 @@ where pub(super) search_graph: &'a mut SearchGraph, - nested_goals: NestedGoals, + nested_goals: Vec<(GoalSource, Goal)>, pub(super) origin_span: I::Span, @@ -129,38 +128,6 @@ where pub(super) inspect: ProofTreeBuilder, } -#[derive_where(Clone, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -struct NestedGoals { - /// These normalizes-to goals are treated specially during the evaluation - /// loop. In each iteration we take the RHS of the projection, replace it with - /// a fresh inference variable, and only after evaluating that goal do we - /// equate the fresh inference variable with the actual RHS of the predicate. - /// - /// This is both to improve caching, and to avoid using the RHS of the - /// projection predicate to influence the normalizes-to candidate we select. - /// - /// Forgetting to replace the RHS with a fresh inference variable when we evaluate - /// this goal results in an ICE.. - pub normalizes_to_goals: Vec>>, - /// The rest of the goals which have not yet processed or remain ambiguous. - pub goals: Vec<(GoalSource, Goal)>, -} - -impl NestedGoals { - fn new() -> Self { - Self { normalizes_to_goals: Vec::new(), goals: Vec::new() } - } - - fn is_empty(&self) -> bool { - self.normalizes_to_goals.is_empty() && self.goals.is_empty() - } -} - #[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GenerateProofTree { @@ -332,7 +299,7 @@ where let mut ecx = EvalCtxt { delegate, search_graph: &mut search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree), // Only relevant when canonicalizing the response, @@ -385,7 +352,7 @@ where predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.canonical.max_universe, search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), origin_span: I::Span::dummy(), tainted: Ok(()), inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), @@ -629,78 +596,83 @@ where /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { let cx = self.cx(); - let mut goals = core::mem::take(&mut self.nested_goals); - // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); - for goal in goals.normalizes_to_goals { - // Replace the goal with an unconstrained infer var, so the - // RHS does not affect projection candidate assembly. - let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); - let unconstrained_goal = goal.with( - cx, - ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, - ); - - let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw( - GoalEvaluationKind::Nested, - GoalSource::TypeRelating, - unconstrained_goal, - )?; - // Add the nested goals from normalization to our own nested goals. - trace!(?nested_goals); - goals.goals.extend(nested_goals); - - // Finally, equate the goal's RHS with the unconstrained var. + for (source, goal) in mem::take(&mut self.nested_goals) { + // We treat normalizes-to goals specially here. In each iteration we take the + // RHS of the projection, replace it with a fresh inference variable, and only + // after evaluating that goal do we equate the fresh inference variable with the + // actual RHS of the predicate. // - // SUBTLE: - // We structurally relate aliases here. This is necessary - // as we otherwise emit a nested `AliasRelate` goal in case the - // returned term is a rigid alias, resulting in overflow. + // This is both to improve caching, and to avoid using the RHS of the + // projection predicate to influence the normalizes-to candidate we select. // - // It is correct as both `goal.predicate.term` and `unconstrained_rhs` - // start out as an unconstrained inference variable so any aliases get - // fully normalized when instantiating it. - // - // FIXME: Strictly speaking this may be incomplete if the normalized-to - // type contains an ambiguous alias referencing bound regions. We should - // consider changing this to only use "shallow structural equality". - self.eq_structurally_relating_aliases( - goal.param_env, - goal.predicate.term, - unconstrained_rhs, - )?; - - // We only look at the `projection_ty` part here rather than - // looking at the "has changed" return from evaluate_goal, - // because we expect the `unconstrained_rhs` part of the predicate - // to have changed -- that means we actually normalized successfully! - let with_resolved_vars = self.resolve_vars_if_possible(goal); - if goal.predicate.alias != with_resolved_vars.predicate.alias { - unchanged_certainty = None; - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.normalizes_to_goals.push(with_resolved_vars); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + // Forgetting to replace the RHS with a fresh inference variable when we evaluate + // this goal results in an ICE. + if let Some(pred) = goal.predicate.as_normalizes_to() { + // We should never encounter higher-ranked normalizes-to goals. + let pred = pred.no_bound_vars().unwrap(); + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = self.next_term_infer_of_kind(pred.term); + let unconstrained_goal = + goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs }); + + let (NestedNormalizationGoals(nested_goals), _, certainty) = + self.evaluate_goal_raw(GoalEvaluationKind::Nested, source, unconstrained_goal)?; + // Add the nested goals from normalization to our own nested goals. + trace!(?nested_goals); + self.nested_goals.extend(nested_goals); + + // Finally, equate the goal's RHS with the unconstrained var. + // + // SUBTLE: + // We structurally relate aliases here. This is necessary + // as we otherwise emit a nested `AliasRelate` goal in case the + // returned term is a rigid alias, resulting in overflow. + // + // It is correct as both `goal.predicate.term` and `unconstrained_rhs` + // start out as an unconstrained inference variable so any aliases get + // fully normalized when instantiating it. + // + // FIXME: Strictly speaking this may be incomplete if the normalized-to + // type contains an ambiguous alias referencing bound regions. We should + // consider changing this to only use "shallow structural equality". + self.eq_structurally_relating_aliases( + goal.param_env, + pred.term, + unconstrained_rhs, + )?; + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + let with_resolved_vars = self.resolve_vars_if_possible(goal); + if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { + unchanged_certainty = None; } - } - } - for (source, goal) in goals.goals { - let (has_changed, certainty) = - self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; - if has_changed == HasChanged::Yes { - unchanged_certainty = None; - } + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, with_resolved_vars)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } else { + let (has_changed, certainty) = + self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; + if has_changed == HasChanged::Yes { + unchanged_certainty = None; + } - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.goals.push((source, goal)); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } } } } @@ -717,23 +689,12 @@ where self.delegate.cx() } - #[instrument(level = "trace", skip(self))] - pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { - goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new( - self, - GoalSource::TypeRelating, - goal.param_env, - )); - self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); - self.nested_goals.normalizes_to_goals.push(goal); - } - #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); - self.nested_goals.goals.push((source, goal)); + self.nested_goals.push((source, goal)); } #[instrument(level = "trace", skip(self, goals))] diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 6a8e0790f7cb4..f22b275bc44a2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -412,20 +412,6 @@ impl, I: Interner> ProofTreeBuilder { } } - pub(crate) fn add_normalizes_to_goal( - &mut self, - delegate: &D, - max_input_universe: ty::UniverseIndex, - goal: Goal>, - ) { - self.add_goal( - delegate, - max_input_universe, - GoalSource::TypeRelating, - goal.with(delegate.cx(), goal.predicate), - ); - } - pub(crate) fn add_goal( &mut self, delegate: &D, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 9466901683e21..cd50b1dece50b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -148,6 +148,47 @@ where } } + fn match_param_env_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution> { + if let Some(projection_pred) = assumption.as_projection_clause() { + if projection_pred.item_def_id() == goal.predicate.def_id() { + let cx = ecx.cx(); + if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.alias.args, + projection_pred.skip_binder().projection_term.args, + ) { + return Err(NoSolution); + } + let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); + ecx.eq( + goal.param_env, + goal.predicate.alias, + assumption_projection_pred.projection_term, + )?; + + ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term); + + // Add GAT where clauses from the trait's definition + // FIXME: We don't need these, since these are the type's own WF obligations. + ecx.add_goals( + GoalSource::AliasWellFormed, + cx.own_predicates_of(goal.predicate.def_id()) + .iter_instantiated(cx, goal.predicate.alias.args) + .map(|pred| goal.with(cx, pred)), + ); + + Ok(()) + } else { + Err(NoSolution) + } + } else { + Err(NoSolution) + } + } + fn consider_additional_alias_assumptions( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 827853be280ed..74a4d1b581721 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -160,6 +160,32 @@ where } } + fn match_param_env_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + assumption: I::Clause, + ) -> Result<(), NoSolution> { + if let Some(trait_clause) = assumption.as_trait_clause() { + if trait_clause.def_id() == goal.predicate.def_id() + && trait_clause.polarity() == goal.predicate.polarity + { + if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( + goal.predicate.trait_ref.args, + trait_clause.skip_binder().trait_ref.args, + ) { + return Err(NoSolution); + } + + let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); + ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref) + } else { + Err(NoSolution) + } + } else { + Err(NoSolution) + } + } + fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -1266,10 +1292,9 @@ where D: SolverDelegate, I: Interner, { - #[instrument(level = "debug", skip(self, goal), ret)] + #[instrument(level = "debug", skip(self), ret)] pub(super) fn merge_trait_candidates( &mut self, - goal: Goal>, mut candidates: Vec>, ) -> Result<(CanonicalResponse, Option), NoSolution> { if let TypingMode::Coherence = self.typing_mode() { @@ -1297,37 +1322,17 @@ where // If there are non-global where-bounds, prefer where-bounds // (including global ones) over everything else. - let has_non_global_where_bounds = candidates.iter().any(|c| match c.source { - CandidateSource::ParamEnv(idx) => { - let where_bound = goal.param_env.caller_bounds().get(idx).unwrap(); - let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else { - unreachable!("expected trait-bound: {where_bound:?}"); - }; - - if trait_pred.has_bound_vars() || !trait_pred.is_global() { - return true; - } - - // We don't consider a trait-bound global if it has a projection bound. - // - // See ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs - // for an example where this is necessary. - for p in goal.param_env.caller_bounds().iter() { - if let ty::ClauseKind::Projection(proj) = p.kind().skip_binder() { - if proj.projection_term.trait_ref(self.cx()) == trait_pred.trait_ref { - return true; - } - } - } - - false - } - _ => false, - }); + let has_non_global_where_bounds = + candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))); if has_non_global_where_bounds { let where_bounds: Vec<_> = candidates .iter() - .filter(|c| matches!(c.source, CandidateSource::ParamEnv(_))) + .filter(|c| { + matches!( + c.source, + CandidateSource::ParamEnv(_) | CandidateSource::GlobalParamEnv(_) + ) + }) .map(|c| c.result) .collect(); return if let Some(response) = self.try_merge_responses(&where_bounds) { @@ -1355,10 +1360,10 @@ where // operate correctly. Otherwise, drop all global where-bounds before merging the // remaining candidates. let proven_via = - if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) { + if candidates.iter().all(|c| matches!(c.source, CandidateSource::GlobalParamEnv(_))) { TraitGoalProvenVia::ParamEnv } else { - candidates.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(_))); + candidates.retain(|c| !matches!(c.source, CandidateSource::GlobalParamEnv(_))); TraitGoalProvenVia::Misc }; @@ -1376,7 +1381,7 @@ where goal: Goal>, ) -> Result<(CanonicalResponse, Option), NoSolution> { let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); - self.merge_trait_candidates(goal, candidates) + self.merge_trait_candidates(candidates) } fn try_stall_coroutine_witness( diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index bc92b95ce7116..4c0c3d8dc5b23 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -993,7 +993,7 @@ pub struct NextSolverConfig { pub coherence: bool = true, /// Whether the new trait solver should be enabled everywhere. /// This is only `true` if `coherence` is also enabled. - pub globally: bool = false, + pub globally: bool = true, } #[derive(Clone)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 36eee5f308656..3f0297e1a72eb 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2564,9 +2564,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/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index cce67b066dde1..332204a0c5f06 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -13,6 +13,49 @@ use crate::errors::NonGenericOpaqueTypeParam; use crate::regions::OutlivesEnvironmentBuildExt; use crate::traits::ObligationCtxt; +pub enum InvalidOpaqueTypeArgs<'tcx> { + AlreadyReported(ErrorGuaranteed), + NotAParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_index: usize, span: Span }, + DuplicateParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_indices: Vec, span: Span }, +} +impl From for InvalidOpaqueTypeArgs<'_> { + fn from(guar: ErrorGuaranteed) -> Self { + InvalidOpaqueTypeArgs::AlreadyReported(guar) + } +} +impl<'tcx> InvalidOpaqueTypeArgs<'tcx> { + pub fn report(self, infcx: &InferCtxt<'tcx>) -> ErrorGuaranteed { + let tcx = infcx.tcx; + match self { + InvalidOpaqueTypeArgs::AlreadyReported(guar) => guar, + InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index, span } => { + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let opaque_param = opaque_generics.param_at(param_index, tcx); + let kind = opaque_param.kind.descr(); + infcx.dcx().emit_err(NonGenericOpaqueTypeParam { + arg: opaque_type_key.args[param_index], + kind, + span, + param_span: tcx.def_span(opaque_param.def_id), + }) + } + InvalidOpaqueTypeArgs::DuplicateParam { opaque_type_key, param_indices, span } => { + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let descr = opaque_generics.param_at(param_indices[0], tcx).kind.descr(); + let spans: Vec<_> = param_indices + .into_iter() + .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) + .collect(); + infcx + .dcx() + .struct_span_err(span, "non-defining opaque type use in defining scope") + .with_span_note(spans, format!("{descr} used multiple times")) + .emit() + } + } + } +} + /// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter]. /// /// [rustc-dev-guide chapter]: @@ -22,23 +65,19 @@ pub fn check_opaque_type_parameter_valid<'tcx>( opaque_type_key: OpaqueTypeKey<'tcx>, span: Span, defining_scope_kind: DefiningScopeKind, -) -> Result<(), ErrorGuaranteed> { +) -> Result<(), InvalidOpaqueTypeArgs<'tcx>> { let tcx = infcx.tcx; - let opaque_generics = tcx.generics_of(opaque_type_key.def_id); let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id); let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); // Avoid duplicate errors in case the opaque has already been malformed in // HIR typeck. if let DefiningScopeKind::MirBorrowck = defining_scope_kind { - if let Err(guar) = infcx + infcx .tcx .type_of_opaque_hir_typeck(opaque_type_key.def_id) .instantiate_identity() - .error_reported() - { - return Err(guar); - } + .error_reported()?; } for (i, arg) in opaque_type_key.iter_captured_args(tcx) { @@ -64,32 +103,18 @@ pub fn check_opaque_type_parameter_valid<'tcx>( } } else { // Prevent `fn foo() -> Foo` from being defining. - let opaque_param = opaque_generics.param_at(i, tcx); - let kind = opaque_param.kind.descr(); - opaque_env.param_is_error(i)?; - - return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam { - arg, - kind, - span, - param_span: tcx.def_span(opaque_param.def_id), - })); + return Err(InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index: i, span }); } } - for (_, indices) in seen_params { - if indices.len() > 1 { - let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); - let spans: Vec<_> = indices - .into_iter() - .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) - .collect(); - return Err(infcx - .dcx() - .struct_span_err(span, "non-defining opaque type use in defining scope") - .with_span_note(spans, format!("{descr} used multiple times")) - .emit()); + for (_, param_indices) in seen_params { + if param_indices.len() > 1 { + return Err(InvalidOpaqueTypeArgs::DuplicateParam { + opaque_type_key, + param_indices, + span, + }); } } diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index 4fdaf740287ba..e03af70e76fe2 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -171,6 +171,7 @@ fn to_selection<'tcx>( } CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested), CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => ImplSource::Param(nested), + CandidateSource::GlobalParamEnv(_) => ImplSource::Param(nested), CandidateSource::CoherenceUnknowable => { span_bug!(span, "didn't expect to select an unknowable candidate") } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 507932699c7cd..2cdc3174f2232 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -9,7 +9,7 @@ use rustc_macros::extension; pub use rustc_middle::traits::query::NormalizationResult; use rustc_middle::ty::{ self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_span::DUMMY_SP; use tracing::{debug, info, instrument}; @@ -127,7 +127,7 @@ struct MaxEscapingBoundVarVisitor { } impl<'tcx> TypeVisitor> for MaxEscapingBoundVarVisitor { - fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { + fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { self.outer_index.shift_in(1); t.super_visit_with(self); self.outer_index.shift_out(1); diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index cf6d2bc151fb0..00101010f14fa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -240,8 +240,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) { continue; } - // FIXME(oli-obk): it is suspicious that we are dropping the constness and - // polarity here. let wc = self.where_clause_may_apply(stack, bound_trait_ref)?; if wc.may_apply() { candidates.vec.push(ParamCandidate(bound)); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 31d69eef5ecf4..0c49ddff39bc2 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -7,7 +7,8 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, fold_regions, + self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, + fold_regions, }; use rustc_span::DUMMY_SP; use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; @@ -185,7 +186,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> { } impl<'tcx> TypeVisitor> for ImplTraitInTraitFinder<'_, 'tcx> { - fn visit_binder>>(&mut self, binder: &ty::Binder<'tcx, T>) { + fn visit_binder>>(&mut self, binder: &ty::Binder<'tcx, T>) { self.depth.shift_in(1); binder.super_visit_with(self); self.depth.shift_out(1); diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 27ea4e211fefe..000cf1e1fd8b1 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -128,7 +128,7 @@ impl> TypeFoldable for Binder { } } -impl> TypeVisitable for Binder { +impl> TypeVisitable for Binder { fn visit_with>(&self, visitor: &mut V) -> V::Result { visitor.visit_binder(self) } @@ -147,7 +147,7 @@ impl> TypeSuperFoldable for Binder { } } -impl> TypeSuperVisitable for Binder { +impl> TypeSuperVisitable for Binder { fn super_visit_with>(&self, visitor: &mut V) -> V::Result { self.as_ref().skip_binder().visit_with(visitor) } @@ -292,7 +292,7 @@ impl ValidateBoundVars { impl TypeVisitor for ValidateBoundVars { type Result = ControlFlow<()>; - fn visit_binder>(&mut self, t: &Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &Binder) -> Self::Result { self.binder_index.shift_in(1); let result = t.super_visit_with(self); self.binder_index.shift_out(1); diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 417803e75ead6..22e535dc0226e 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -299,6 +299,14 @@ pub trait GenericArg>: + From + From { + fn as_term(&self) -> Option { + match self.kind() { + ty::GenericArgKind::Lifetime(_) => None, + ty::GenericArgKind::Type(ty) => Some(ty.into()), + ty::GenericArgKind::Const(ct) => Some(ct.into()), + } + } + fn as_type(&self) -> Option { if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None } } @@ -442,6 +450,14 @@ pub trait Predicate>: { fn as_clause(self) -> Option; + fn as_normalizes_to(self) -> Option>> { + let kind = self.kind(); + match kind.skip_binder() { + ty::PredicateKind::NormalizesTo(pred) => Some(kind.rebind(pred)), + _ => None, + } + } + // FIXME: Eventually uplift the impl out of rustc and make this defaulted. fn allow_normalization(self) -> bool; } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 4e9b87fdf74da..133b4a098c6e4 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -161,6 +161,7 @@ pub enum CandidateSource { /// } /// ``` ParamEnv(usize), + GlobalParamEnv(usize), /// If the self type is an alias type, e.g. an opaque type or a projection, /// we know the bounds on that alias to hold even without knowing its concrete /// underlying type. diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index d1ca9bdb7fbd1..8ba985d2d1931 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -342,7 +342,7 @@ struct HasRegionsBoundAt { // FIXME: Could be optimized to not walk into components with no escaping bound vars. impl TypeVisitor for HasRegionsBoundAt { type Result = ControlFlow<()>; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { self.binder.shift_in(1); t.super_visit_with(self)?; self.binder.shift_out(1); diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 2285e0e75de04..ccb84e2591122 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -52,7 +52,7 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use crate::inherent::*; -use crate::{self as ty, Interner, TypeFlags}; +use crate::{self as ty, Interner, TypeFlags, TypeFoldable}; /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. @@ -94,7 +94,7 @@ pub trait TypeVisitor: Sized { #[cfg(not(feature = "nightly"))] type Result: VisitorResult; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { t.super_visit_with(self) } @@ -401,7 +401,7 @@ impl std::fmt::Debug for HasTypeFlagsVisitor { impl TypeVisitor for HasTypeFlagsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { // If we're looking for the HAS_BINDER_VARS flag, check if the // binder has vars. This won't be present in the binder's bound // value, so we need to check here too. @@ -510,7 +510,7 @@ struct HasEscapingVarsVisitor { impl TypeVisitor for HasEscapingVarsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { self.outer_index.shift_in(1); let result = t.super_visit_with(self); self.outer_index.shift_out(1); diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 8db9cd593b335..da09edd7f7c03 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -20,8 +20,8 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -915,7 +915,7 @@ pub fn for_each_top_level_late_bound_region( ControlFlow::Continue(()) } } - fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { + fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result { self.index += 1; let res = t.super_visit_with(self); self.index -= 1; diff --git a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr index 3d667f12371ab..4bb9047b3035d 100644 --- a/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr +++ b/tests/ui/closures/deduce-signature/obligation-with-leaking-placeholders.next.stderr @@ -5,7 +5,7 @@ LL | needs_foo(|x| { | ^ ... LL | x.to_string(); - | - type must be known at this point + | --------- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index 6170250992ce2..8178f54b2aab1 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -18,13 +18,16 @@ LL | where LL | T: AsExpression, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check` -error[E0271]: type mismatch resolving `Integer == Text` +error[E0277]: the trait bound `&str: AsExpression` is not satisfied --> $DIR/as_expression.rs:56:5 | LL | SelectInt.check("bar"); - | ^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `AsExpression` is not implemented for `&str` + | + = help: the trait `AsExpression` is not implemented for `&str` + but trait `AsExpression` is implemented for it + = help: for that trait implementation, expected `Text`, found `Integer` error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs index 673adb82870d8..86f39e43484e7 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs @@ -55,5 +55,5 @@ impl Foo for T where T: Expression {} fn main() { SelectInt.check("bar"); //~^ ERROR the trait bound `&str: AsExpression` is not satisfied - //[next]~| ERROR type mismatch + //[next]~| ERROR the trait bound `&str: AsExpression` is not satisfied } diff --git a/tests/ui/impl-trait/call_method_ambiguous.next.stderr b/tests/ui/impl-trait/call_method_ambiguous.next.stderr index 5251555f57421..0def594daf1cc 100644 --- a/tests/ui/impl-trait/call_method_ambiguous.next.stderr +++ b/tests/ui/impl-trait/call_method_ambiguous.next.stderr @@ -5,7 +5,7 @@ LL | let mut iter = foo(n - 1, m); | ^^^^^^^^ LL | LL | assert_eq!(iter.get(), 1); - | ---- type must be known at this point + | --- type must be known at this point | help: consider giving `iter` an explicit type | diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr index 271051f120abc..7bbf5f5153a59 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr @@ -5,7 +5,7 @@ LL | let x = my_foo(); | ^ LL | LL | x.my_debug(); - | - type must be known at this point + | -------- type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr index 7202cb6f90a6f..5dea3a715e9bc 100644 --- a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr @@ -5,7 +5,7 @@ LL | let x = my_foo(); | ^ LL | LL | x.my_debug(); - | - type must be known at this point + | -------- type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr index dca0a7b0a1a9f..cb383b2db3893 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.default.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr index dca0a7b0a1a9f..cb383b2db3893 100644 --- a/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr +++ b/tests/ui/impl-trait/hidden-type-is-opaque-2.next.stderr @@ -5,7 +5,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -19,7 +19,7 @@ LL | Thunk::new(|mut cont| { | ^^^^^^^^ LL | LL | cont.reify_as(); - | ---- type must be known at this point + | -------- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/impl-trait/method-resolution4.next.stderr b/tests/ui/impl-trait/method-resolution4.next.stderr deleted file mode 100644 index 0524f49f98e58..0000000000000 --- a/tests/ui/impl-trait/method-resolution4.next.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/method-resolution4.rs:13:9 - | -LL | foo(false).next().unwrap(); - | ^^^^^^^^^^ cannot infer type - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/method-resolution4.rs b/tests/ui/impl-trait/method-resolution4.rs index 90e7850cad51e..f90a9309cdab8 100644 --- a/tests/ui/impl-trait/method-resolution4.rs +++ b/tests/ui/impl-trait/method-resolution4.rs @@ -6,12 +6,11 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver -//@[current] check-pass +//@ check-pass fn foo(b: bool) -> impl Iterator { if b { foo(false).next().unwrap(); - //[next]~^ ERROR type annotations needed } std::iter::empty() } diff --git a/tests/ui/impl-trait/recursive-bound-eval.next.stderr b/tests/ui/impl-trait/recursive-bound-eval.next.stderr index 4bab290d71c3c..e94f0a59a1d22 100644 --- a/tests/ui/impl-trait/recursive-bound-eval.next.stderr +++ b/tests/ui/impl-trait/recursive-bound-eval.next.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/recursive-bound-eval.rs:20:13 + --> $DIR/recursive-bound-eval.rs:20:28 | LL | move || recursive_fn().parse() - | ^^^^^^^^^^^^^^ cannot infer type + | ^^^^^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr index 10056bdf3d4f4..ba1c81c4518a7 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-0.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-0.rs:21:6 | LL | f(|a, b| a.cmp(b)); - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr index 632a9b99f84ef..93bba3625b540 100644 --- a/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr +++ b/tests/ui/inference/need_type_info/incompat-call-after-qualified-path-1.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/incompat-call-after-qualified-path-1.rs:25:6 | LL | f(|a, b| a.cmp(b)); - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/issues/issue-2151.stderr b/tests/ui/issues/issue-2151.stderr index b130f162414d0..59fef42eb5e8b 100644 --- a/tests/ui/issues/issue-2151.stderr +++ b/tests/ui/issues/issue-2151.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x = panic!(); | ^ LL | x.clone(); - | - type must be known at this point + | ----- type must be known at this point | help: consider giving `x` an explicit type | diff --git a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr index 117d189867bd7..539673bc343ce 100644 --- a/tests/ui/lazy-type-alias-impl-trait/branches3.stderr +++ b/tests/ui/lazy-type-alias-impl-trait/branches3.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:9:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:18:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -24,7 +24,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:26:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -35,7 +35,7 @@ error[E0282]: type annotations needed --> $DIR/branches3.rs:33:10 | LL | |s| s.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.gai.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.gai.stderr index de38476c82b30..a962d47f910d5 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.gai.stderr +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.gai.stderr @@ -5,7 +5,7 @@ LL | let x = [Foo(PhantomData); 2]; | ^ LL | LL | _ = extract(x).max(2); - | ---------- type must be known at this point + | --- type must be known at this point | help: consider giving `x` an explicit type, where the type for type parameter `T` is specified | diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr index 6559845c23ec5..c2c6f2b99bc24 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x: Option<_> = None; | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` LL | x.unwrap().method_that_could_exist_on_some_type(); - | ---------- type must be known at this point + | ------------------------------------ type must be known at this point | help: consider specifying the generic argument | @@ -16,6 +16,8 @@ error[E0282]: type annotations needed | LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` +LL | .to_string() + | --------- type must be known at this point | error: aborting due to 2 previous errors diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr index 6559845c23ec5..c2c6f2b99bc24 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed LL | let x: Option<_> = None; | ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option` LL | x.unwrap().method_that_could_exist_on_some_type(); - | ---------- type must be known at this point + | ------------------------------------ type must be known at this point | help: consider specifying the generic argument | @@ -16,6 +16,8 @@ error[E0282]: type annotations needed | LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` +LL | .to_string() + | --------- type must be known at this point | error: aborting due to 2 previous errors diff --git a/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs new file mode 100644 index 0000000000000..b72124b49a580 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs @@ -0,0 +1,45 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// A regression test for trait-system-refactor-initiative#184. +// +// When adding nested goals we replace aliases with infer vars +// and add `AliasRelate` goals to constrain them. When doing this +// for `NormalizesTo` goals, we then first tries to prove the +// `NormalizesTo` goal and then normalized the nested aliases. + +trait Trait { + type Assoc; +} +impl Trait for T { + type Assoc = (); +} + +trait Id { + type This; +} +impl Id for T { + type This = T; +} +trait Relate { + type Alias; +} +impl Relate for T { + type Alias = ::This>>::Assoc; +} + + +fn guide_me>() { + // Normalizing this emits an `>::Alias` ends up relating + // the associated type with the unconstrained term. This resulted in a + // `NormalizesTo(::This>>::Assoc, ?x)` goal. We replace + // `::This` with an infer var `?y`, resulting in the following goals: + // - `NormalizesTo(::Assoc, ?x)` + // - `AliasRelate(::This, ?y)` + // + // When proving the `NormalizesTo` goal first, we incompletely constrain `?y` + // to `u32`, causing an unexpected type mismatch. + let _: >::Alias; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs index 5284220ac38fe..3150d9a88d0e8 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs +++ b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ compile-flags: -Znext-solver // When canonicalizing a response in the trait solver, we bail with overflow // if there are too many non-region inference variables. Doing so in normalizes-to diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr index e91a48f62aec3..f5fd9ce9864ce 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr @@ -4,24 +4,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Trait` LL | ::Assoc: Trait, | ^^^^^ -error[E0275]: overflow evaluating the requirement `::Assoc well-formed` - --> $DIR/normalize-param-env-4.rs:19:26 - | -LL | ::Assoc: Trait, - | ^^^^^ - -error[E0275]: overflow evaluating the requirement `T: Trait` - --> $DIR/normalize-param-env-4.rs:32:19 - | -LL | impls_trait::(); - | ^ - | -note: required by a bound in `impls_trait` - --> $DIR/normalize-param-env-4.rs:15:19 - | -LL | fn impls_trait() {} - | ^^^^^ required by this bound in `impls_trait` - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr index 849ffd214f07d..559bc57d90638 100644 --- a/tests/ui/type-alias-impl-trait/closures_in_branches.stderr +++ b/tests/ui/type-alias-impl-trait/closures_in_branches.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:8:10 | LL | |x| x.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | @@ -13,7 +13,7 @@ error[E0282]: type annotations needed --> $DIR/closures_in_branches.rs:22:10 | LL | |x| x.len() - | ^ - type must be known at this point + | ^ --- type must be known at this point | help: consider giving this closure parameter an explicit type | diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs index 4391bf01dc91f..7a540d2a57495 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs @@ -22,7 +22,7 @@ impl Trait for Out { type Out = Out; #[define_opaque(Bar)] fn convert(_i: In) -> Self::Out { - //[next]~^ ERROR: cannot satisfy `Bar == _` + //[next]~^ ERROR: type annotations needed: cannot satisfy `Bar == _` //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}` unreachable!(); } diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr index bbdd3923821ff..480634d97d5be 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr @@ -1,9 +1,9 @@ -error[E0282]: type annotations needed +error[E0284]: type annotations needed: cannot satisfy `Tait == _` --> $DIR/method_resolution_trait_method_from_opaque.rs:28:9 | LL | self.bar.next().unwrap(); - | ^^^^^^^^ cannot infer type + | ^^^^^^^^ cannot satisfy `Tait == _` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr index 92ad83c330000..ac7100ad1f5a3 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr @@ -4,6 +4,20 @@ error[E0308]: mismatched types LL | fn func() -> [(); { () }] { | ^^ expected `usize`, found `()` +error[E0271]: type mismatch resolving `N == { () }` + --> $DIR/const-in-impl-fn-return-type.rs:20:5 + | +LL | fn func() -> [(); { () }] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | +note: the requirement `N == { () }` appears on the `impl`'s method `func` but not on the corresponding trait's method + --> $DIR/const-in-impl-fn-return-type.rs:12:8 + | +LL | trait Trait { + | ----- in this trait +LL | fn func() -> [(); N]; + | ^^^^ this trait's method doesn't have the requirement `N == { () }` + error: the constant `N` is not of type `usize` --> $DIR/const-in-impl-fn-return-type.rs:12:32 | @@ -12,6 +26,7 @@ LL | fn func() -> [(); N]; | = note: the length of array `[(); N]` must be type `usize` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs index 6bbac9d45bbe5..5a188602847e6 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs @@ -19,6 +19,7 @@ struct S {} impl Trait for S { fn func() -> [(); { () }] { //~^ ERROR mismatched types + //[next]~| ERROR type mismatch resolving `N == { () }` N } } diff --git a/tests/ui/typeck/issue-13853.rs b/tests/ui/typeck/issue-13853.rs index ac9886d2e7249..aebbdf05c9881 100644 --- a/tests/ui/typeck/issue-13853.rs +++ b/tests/ui/typeck/issue-13853.rs @@ -25,7 +25,7 @@ impl Node for Stuff { fn iterate>(graph: &G) { for node in graph.iter() { //~ ERROR no method named `iter` found - node.zomg(); + node.zomg(); //~ERROR type annotations needed } } diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr index 45363c87d29df..9b8698d6ed2c0 100644 --- a/tests/ui/typeck/issue-13853.stderr +++ b/tests/ui/typeck/issue-13853.stderr @@ -17,6 +17,12 @@ error[E0599]: no method named `iter` found for reference `&G` in the current sco LL | for node in graph.iter() { | ^^^^ method not found in `&G` +error[E0282]: type annotations needed + --> $DIR/issue-13853.rs:28:14 + | +LL | node.zomg(); + | ^^^^ cannot infer type + error[E0308]: mismatched types --> $DIR/issue-13853.rs:37:13 | @@ -37,7 +43,7 @@ help: consider borrowing here LL | iterate(&graph); | + -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0308, E0599. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0282, E0308, E0599. +For more information about an error, try `rustc --explain E0282`.