From 7e15ceeb4793c64f2dc49eebdbef0850197b709b Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 7 Apr 2025 15:37:39 +0200 Subject: [PATCH 01/17] support revealing uses of opaque types --- compiler/rustc_borrowck/messages.ftl | 6 - compiler/rustc_borrowck/src/consumers.rs | 6 +- compiler/rustc_borrowck/src/dataflow.rs | 44 +- .../src/diagnostics/region_errors.rs | 43 - compiler/rustc_borrowck/src/lib.rs | 152 ++- .../rustc_borrowck/src/member_constraints.rs | 226 ---- compiler/rustc_borrowck/src/nll.rs | 81 +- .../rustc_borrowck/src/region_infer/mod.rs | 299 +----- .../src/region_infer/opaque_types.rs | 981 ++++++++++++++---- .../src/region_infer/reverse_sccs.rs | 13 - .../rustc_borrowck/src/region_infer/values.rs | 3 +- compiler/rustc_borrowck/src/root_cx.rs | 152 ++- .../rustc_borrowck/src/session_diagnostics.rs | 15 +- .../src/type_check/canonical.rs | 109 +- .../src/type_check/free_region_relations.rs | 9 +- compiler/rustc_borrowck/src/type_check/mod.rs | 86 +- .../src/type_check/opaque_types.rs | 333 ------ .../rustc_borrowck/src/universal_regions.rs | 4 +- compiler/rustc_data_structures/src/frozen.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 4 + compiler/rustc_middle/src/ty/context.rs | 5 - compiler/rustc_middle/src/ty/mod.rs | 4 +- compiler/rustc_session/src/options.rs | 3 - .../non-root-universe-existential-1.rs | 31 + .../non-root-universe-existential-2.rs | 31 + 25 files changed, 1281 insertions(+), 1361 deletions(-) delete mode 100644 compiler/rustc_borrowck/src/member_constraints.rs delete mode 100644 compiler/rustc_borrowck/src/type_check/opaque_types.rs create mode 100644 tests/ui/nll/member-constraints/non-root-universe-existential-1.rs create mode 100644 tests/ui/nll/member-constraints/non-root-universe-existential-2.rs diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index 33b80c4b03d6f..660f4c50c6b5d 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -156,12 +156,6 @@ borrowck_moved_due_to_usage_in_operator = *[false] operator } -borrowck_opaque_type_lifetime_mismatch = - opaque type used twice with different lifetimes - .label = lifetime `{$arg}` used here - .prev_lifetime_label = lifetime `{$prev}` previously used here - .note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types - borrowck_partial_var_move_by_use_in_closure = variable {$is_partial -> [true] partially moved diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 1f087b092346e..ea83e82c339e9 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -15,7 +15,7 @@ pub use super::polonius::legacy::{ RichLocation, RustcFacts, }; pub use super::region_infer::RegionInferenceContext; -use crate::{BorrowCheckRootCtxt, do_mir_borrowck}; +use crate::BorrowCheckRootCtxt; /// Options determining the output behavior of [`get_body_with_borrowck_facts`]. /// @@ -101,6 +101,6 @@ pub fn get_body_with_borrowck_facts( def_id: LocalDefId, options: ConsumerOptions, ) -> BodyWithBorrowckFacts<'_> { - let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id); - *do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap() + let root_cx = BorrowCheckRootCtxt::new(tcx, def_id); + *root_cx.borrowck_root(Some(options)).1.unwrap() } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 7511a55b03ae5..7c508ff0902bb 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -1,7 +1,6 @@ use std::fmt; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::graph; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges, @@ -317,9 +316,8 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { loans_out_of_scope_at_location: FxIndexMap::default(), }; for (loan_idx, loan_data) in borrow_set.iter_enumerated() { - let issuing_region = loan_data.region; let loan_issued_at = loan_data.reserve_location; - prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at); + prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at); } prec.loans_out_of_scope_at_location @@ -328,45 +326,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { /// Loans are in scope while they are live: whether they are contained within any live region. /// In the location-insensitive analysis, a loan will be contained in a region if the issuing /// region can reach it in the subset graph. So this is a reachability problem. - fn precompute_loans_out_of_scope( - &mut self, - loan_idx: BorrowIndex, - issuing_region: RegionVid, - loan_issued_at: Location, - ) { - let sccs = self.regioncx.constraint_sccs(); - let universal_regions = self.regioncx.universal_regions(); - - // The loop below was useful for the location-insensitive analysis but shouldn't be - // impactful in the location-sensitive case. It seems that it does, however, as without it a - // handful of tests fail. That likely means some liveness or outlives data related to choice - // regions is missing - // FIXME: investigate the impact of loans traversing applied member constraints and why some - // tests fail otherwise. - // - // We first handle the cases where the loan doesn't go out of scope, depending on the - // issuing region's successors. - for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) { - // Via applied member constraints - // - // The issuing region can flow into the choice regions, and they are either: - // - placeholders or free regions themselves, - // - or also transitively outlive a free region. - // - // That is to say, if there are applied member constraints here, the loan escapes the - // function and cannot go out of scope. We could early return here. - // - // For additional insurance via fuzzing and crater, we verify that the constraint's min - // choice indeed escapes the function. In the future, we could e.g. turn this check into - // a debug assert and early return as an optimization. - let scc = sccs.scc(successor); - for constraint in self.regioncx.applied_member_constraints(scc) { - if universal_regions.is_universal_region(constraint.min_choice) { - return; - } - } - } - + fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) { let first_block = loan_issued_at.block; let first_bb_data = &self.body.basic_blocks[first_block]; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3bec07afa0fe0..0e481012e7290 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -23,7 +23,6 @@ use rustc_trait_selection::error_reporting::infer::nice_region_error::{ self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, }; -use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; @@ -84,9 +83,6 @@ impl<'tcx> RegionErrors<'tcx> { let guar = self.1.sess.dcx().delayed_bug(format!("{val:?}")); self.0.push((val, guar)); } - pub(crate) fn is_empty(&self) -> bool { - self.0.is_empty() - } pub(crate) fn into_iter( self, ) -> impl Iterator, ErrorGuaranteed)> { @@ -108,18 +104,6 @@ pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, - /// An unexpected hidden region for an opaque type. - UnexpectedHiddenRegion { - /// The span for the member constraint. - span: Span, - /// The hidden type. - hidden_ty: Ty<'tcx>, - /// The opaque type. - key: ty::OpaqueTypeKey<'tcx>, - /// The unexpected region. - member_region: ty::Region<'tcx>, - }, - /// Higher-ranked subtyping error. BoundUniversalRegionError { /// The placeholder free region. @@ -312,9 +296,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // buffered in the `MirBorrowckCtxt`. let mut outlives_suggestion = OutlivesSuggestionBuilder::default(); - let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = - None; - for (nll_error, _) in nll_errors.into_iter() { match nll_error { RegionErrorKind::TypeTestError { type_test } => { @@ -364,30 +345,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => { - let named_ty = - self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty); - let named_key = - self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, key); - let named_region = self - .regioncx - .name_regions_for_member_constraint(self.infcx.tcx, member_region); - let diag = unexpected_hidden_region_diagnostic( - self.infcx, - self.mir_def_id(), - span, - named_ty, - named_region, - named_key, - ); - if last_unexpected_hidden_region != Some((span, named_ty, named_key)) { - self.buffer_error(diag); - last_unexpected_hidden_region = Some((span, named_ty, named_key)); - } else { - diag.delay_as_bug(); - } - } - RegionErrorKind::BoundUniversalRegionError { longer_fr, placeholder, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 676cb618b725b..15b7cad26de1a 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -19,10 +19,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; @@ -31,6 +35,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, }; @@ -46,14 +51,17 @@ use rustc_mir_dataflow::impls::{ use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex, }; +use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use smallvec::SmallVec; use tracing::{debug, instrument}; +use type_check::free_region_relations::UniversalRegionRelations; +use type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults}; use crate::borrow_set::{BorrowData, BorrowSet}; -use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions}; +use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions, RustcFacts}; use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows}; use crate::diagnostics::{ AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName, @@ -61,8 +69,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; @@ -74,7 +84,6 @@ mod constraints; mod dataflow; mod def_use; mod diagnostics; -mod member_constraints; mod nll; mod path_utils; mod place_ext; @@ -125,20 +134,8 @@ fn mir_borrowck( let opaque_types = ConcreteOpaqueTypes(Default::default()); Ok(tcx.arena.alloc(opaque_types)) } else { - let mut root_cx = BorrowCheckRootCtxt::new(tcx, def); - // We need to manually borrowck all nested bodies from the HIR as - // we do not generate MIR for dead code. Not doing so causes us to - // never check closures in dead code. - let nested_bodies = tcx.nested_bodies_within(def); - for def_id in nested_bodies { - root_cx.get_or_insert_nested(def_id); - } - - let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } = - do_mir_borrowck(&mut root_cx, def, None).0; - debug_assert!(closure_requirements.is_none()); - debug_assert!(used_mut_upvars.is_empty()); - root_cx.finalize() + let root_cx = BorrowCheckRootCtxt::new(tcx, def); + root_cx.borrowck_root(None).0 } } @@ -150,6 +147,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 @@ -288,6 +287,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 @@ -296,11 +313,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); @@ -321,6 +338,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); @@ -331,6 +349,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, @@ -340,18 +432,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); @@ -577,12 +674,7 @@ pub(crate) struct BorrowckInferCtxt<'tcx> { impl<'tcx> BorrowckInferCtxt<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { - let typing_mode = if tcx.use_typing_mode_borrowck() { - TypingMode::borrowck(tcx, def_id) - } else { - TypingMode::analysis_in_body(tcx, def_id) - }; - let infcx = tcx.infer_ctxt().build(typing_mode); + let infcx = tcx.infer_ctxt().build(TypingMode::borrowck(tcx, def_id)); let param_env = tcx.param_env(def_id); BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env } } diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs deleted file mode 100644 index bdd0f6fe11e0f..0000000000000 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ /dev/null @@ -1,226 +0,0 @@ -use std::hash::Hash; -use std::ops::Index; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; -use tracing::instrument; - -/// Compactly stores a set of `R0 member of [R1...Rn]` constraints, -/// indexed by the region `R0`. -#[derive(Debug)] -pub(crate) struct MemberConstraintSet<'tcx, R> -where - R: Copy + Eq, -{ - /// Stores the first "member" constraint for a given `R0`. This is an - /// index into the `constraints` vector below. - first_constraints: FxIndexMap, - - /// Stores the data about each `R0 member of [R1..Rn]` constraint. - /// These are organized into a linked list, so each constraint - /// contains the index of the next constraint with the same `R0`. - constraints: IndexVec>, - - /// Stores the `R1..Rn` regions for *all* sets. For any given - /// constraint, we keep two indices so that we can pull out a - /// slice. - choice_regions: Vec, -} - -/// Represents a `R0 member of [R1..Rn]` constraint -#[derive(Debug)] -pub(crate) struct MemberConstraint<'tcx> { - next_constraint: Option, - - /// The span where the hidden type was instantiated. - pub(crate) definition_span: Span, - - /// The hidden type in which `R0` appears. (Used in error reporting.) - pub(crate) hidden_ty: Ty<'tcx>, - - pub(crate) key: ty::OpaqueTypeKey<'tcx>, - - /// The region `R0`. - pub(crate) member_region_vid: ty::RegionVid, - - /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`. - start_index: usize, - - /// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`. - end_index: usize, -} - -rustc_index::newtype_index! { - #[debug_format = "MemberConstraintIndex({})"] - pub(crate) struct NllMemberConstraintIndex {} -} - -impl Default for MemberConstraintSet<'_, ty::RegionVid> { - fn default() -> Self { - Self { - first_constraints: Default::default(), - constraints: Default::default(), - choice_regions: Default::default(), - } - } -} - -impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { - pub(crate) fn is_empty(&self) -> bool { - self.constraints.is_empty() - } - - /// Pushes a member constraint into the set. - #[instrument(level = "debug", skip(self))] - pub(crate) fn add_member_constraint( - &mut self, - key: ty::OpaqueTypeKey<'tcx>, - hidden_ty: Ty<'tcx>, - definition_span: Span, - member_region_vid: ty::RegionVid, - choice_regions: &[ty::RegionVid], - ) { - let next_constraint = self.first_constraints.get(&member_region_vid).cloned(); - let start_index = self.choice_regions.len(); - self.choice_regions.extend(choice_regions); - let end_index = self.choice_regions.len(); - let constraint_index = self.constraints.push(MemberConstraint { - next_constraint, - member_region_vid, - definition_span, - hidden_ty, - key, - start_index, - end_index, - }); - self.first_constraints.insert(member_region_vid, constraint_index); - } -} - -impl<'tcx, R1> MemberConstraintSet<'tcx, R1> -where - R1: Copy + Hash + Eq, -{ - /// Remap the "member region" key using `map_fn`, producing a new - /// member constraint set. This is used in the NLL code to map from - /// the original `RegionVid` to an scc index. In some cases, we - /// may have multiple `R1` values mapping to the same `R2` key -- that - /// is ok, the two sets will be merged. - pub(crate) fn into_mapped( - self, - mut map_fn: impl FnMut(R1) -> R2, - ) -> MemberConstraintSet<'tcx, R2> - where - R2: Copy + Hash + Eq, - { - // We can re-use most of the original data, just tweaking the - // linked list links a bit. - // - // For example if we had two keys `Ra` and `Rb` that both now - // wind up mapped to the same key `S`, we would append the - // linked list for `Ra` onto the end of the linked list for - // `Rb` (or vice versa) -- this basically just requires - // rewriting the final link from one list to point at the other - // other (see `append_list`). - - let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self; - - let mut first_constraints2 = FxIndexMap::default(); - first_constraints2.reserve(first_constraints.len()); - - for (r1, start1) in first_constraints { - let r2 = map_fn(r1); - if let Some(&start2) = first_constraints2.get(&r2) { - append_list(&mut constraints, start1, start2); - } - first_constraints2.insert(r2, start1); - } - - MemberConstraintSet { first_constraints: first_constraints2, constraints, choice_regions } - } -} - -impl<'tcx, R> MemberConstraintSet<'tcx, R> -where - R: Copy + Hash + Eq, -{ - pub(crate) fn all_indices(&self) -> impl Iterator { - self.constraints.indices() - } - - /// Iterate down the constraint indices associated with a given - /// peek-region. You can then use `choice_regions` and other - /// methods to access data. - pub(crate) fn indices( - &self, - member_region_vid: R, - ) -> impl Iterator { - let mut next = self.first_constraints.get(&member_region_vid).cloned(); - std::iter::from_fn(move || -> Option { - if let Some(current) = next { - next = self.constraints[current].next_constraint; - Some(current) - } else { - None - } - }) - } - - /// Returns the "choice regions" for a given member - /// constraint. This is the `R1..Rn` from a constraint like: - /// - /// ```text - /// R0 member of [R1..Rn] - /// ``` - pub(crate) fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { - let MemberConstraint { start_index, end_index, .. } = &self.constraints[pci]; - &self.choice_regions[*start_index..*end_index] - } -} - -impl<'tcx, R> Index for MemberConstraintSet<'tcx, R> -where - R: Copy + Eq, -{ - type Output = MemberConstraint<'tcx>; - - fn index(&self, i: NllMemberConstraintIndex) -> &MemberConstraint<'tcx> { - &self.constraints[i] - } -} - -/// Given a linked list starting at `source_list` and another linked -/// list starting at `target_list`, modify `target_list` so that it is -/// followed by `source_list`. -/// -/// Before: -/// -/// ```text -/// target_list: A -> B -> C -> (None) -/// source_list: D -> E -> F -> (None) -/// ``` -/// -/// After: -/// -/// ```text -/// target_list: A -> B -> C -> D -> E -> F -> (None) -/// ``` -fn append_list( - constraints: &mut IndexSlice>, - target_list: NllMemberConstraintIndex, - source_list: NllMemberConstraintIndex, -) { - let mut p = target_list; - loop { - let r = &mut constraints[p]; - match r.next_constraint { - Some(q) => p = q, - None => { - r.next_constraint = Some(source_list); - return; - } - } - } -} diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index fe899bb054fa9..74a993d7d3610 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -5,14 +5,13 @@ use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; -use polonius_engine::{Algorithm, Output}; +use polonius_engine::{Algorithm, AllFacts, Output}; +use rustc_data_structures::frozen::Frozen; use rustc_index::IndexSlice; use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options}; use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TyCtxt}; -use rustc_mir_dataflow::ResultsCursor; -use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_session::config::MirIncludeSpans; @@ -20,18 +19,19 @@ use rustc_span::sym; use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; -use crate::consumers::ConsumerOptions; +use crate::consumers::{ConsumerOptions, RustcFacts}; use crate::diagnostics::RegionErrors; -use crate::polonius::PoloniusDiagnosticsContext; use crate::polonius::legacy::{ PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, }; +use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext}; use crate::region_infer::RegionInferenceContext; -use crate::type_check::{self, MirTypeckResults}; +use crate::type_check::MirTypeckRegionConstraints; +use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; use crate::{ - BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements, - polonius, renumber, + BorrowckInferCtxt, BorrowckState, ClosureOutlivesSubject, ClosureRegionRequirements, polonius, + renumber, }; /// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any @@ -72,51 +72,47 @@ pub(crate) fn replace_regions_in_mir<'tcx>( universal_regions } -/// Computes the (non-lexical) regions from the input MIR. +pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( + partial_result: &BorrowckState<'tcx>, +) -> Option> { + let BorrowckState { + infcx, + body_owned, + location_map, + universal_region_relations, + constraints, + .. + } = partial_result; + + let mut regioncx = RegionInferenceContext::new( + &infcx, + constraints.clone(), + universal_region_relations.clone(), + location_map.clone(), + ); + let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, &body_owned, None); + closure_region_requirements +} + +/// Computes and checks the region graph for the given constraints. /// /// This may result in errors being reported. -pub(crate) fn compute_regions<'a, 'tcx>( - root_cx: &mut BorrowCheckRootCtxt<'tcx>, +pub(crate) fn compute_regions<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, - universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, - promoted: &IndexSlice>, location_table: &PoloniusLocationTable, - flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, + location_map: Rc, consumer_options: Option, + universal_region_relations: Frozen>, + constraints: MirTypeckRegionConstraints<'tcx>, + mut polonius_facts: Option>, + polonius_context: Option, ) -> NllOutput<'tcx> { let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); - let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default() - || is_polonius_legacy_enabled; let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default() || is_polonius_legacy_enabled; - let mut polonius_facts = - (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default()); - - let location_map = Rc::new(DenseLocationMap::new(body)); - - // Run the MIR type-checker. - let MirTypeckResults { - constraints, - universal_region_relations, - opaque_type_values, - polonius_context, - } = type_check::type_check( - root_cx, - infcx, - body, - promoted, - universal_regions, - location_table, - borrow_set, - &mut polonius_facts, - flow_inits, - move_data, - Rc::clone(&location_map), - ); - // If requested, emit legacy polonius facts. polonius::legacy::emit_facts( &mut polonius_facts, @@ -164,12 +160,9 @@ pub(crate) fn compute_regions<'a, 'tcx>( regioncx.solve(infcx, body, polonius_output.clone()); if let Some(guar) = nll_errors.has_errors() { - // Suppress unhelpful extra errors in `infer_opaque_types`. infcx.set_tainted_by_errors(guar); } - regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values); - NllOutput { regioncx, polonius_input: polonius_facts.map(Box::new), diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index b4ff3d66f3d5b..23a67e2b42d27 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1,7 +1,6 @@ use std::collections::VecDeque; use std::rc::Rc; -use rustc_data_structures::binary_search_util; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::scc::{self, Sccs}; @@ -23,14 +22,12 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::{DUMMY_SP, Span}; use tracing::{Level, debug, enabled, instrument, trace}; -use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph}; +use crate::constraints::graph::NormalConstraintGraph; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; -use crate::region_infer::reverse_sccs::ReverseSccGraph; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints}; @@ -42,7 +39,7 @@ use crate::{ mod dump_mir; mod graphviz; -mod opaque_types; +pub(crate) mod opaque_types; mod reverse_sccs; pub(crate) mod values; @@ -195,20 +192,6 @@ pub struct RegionInferenceContext<'tcx> { scc_annotations: IndexVec, - /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if - /// `B: A`. This is used to compute the universal regions that are required - /// to outlive a given SCC. Computed lazily. - rev_scc_graph: Option, - - /// The "R0 member of [R1..Rn]" constraints, indexed by SCC. - member_constraints: Rc>, - - /// Records the member constraints that we applied to each scc. - /// This is useful for error reporting. Once constraint - /// propagation is done, this vector is sorted according to - /// `member_region_scc`. - member_constraints_applied: Vec, - /// Map universe indexes to information on why we created it. universe_causes: FxIndexMap>, @@ -225,32 +208,6 @@ pub struct RegionInferenceContext<'tcx> { universal_region_relations: Frozen>, } -/// Each time that `apply_member_constraint` is successful, it appends -/// one of these structs to the `member_constraints_applied` field. -/// This is used in error reporting to trace out what happened. -/// -/// The way that `apply_member_constraint` works is that it effectively -/// adds a new lower bound to the SCC it is analyzing: so you wind up -/// with `'R: 'O` where `'R` is the pick-region and `'O` is the -/// minimal viable option. -#[derive(Debug)] -pub(crate) struct AppliedMemberConstraint { - /// The SCC that was affected. (The "member region".) - /// - /// The vector if `AppliedMemberConstraint` elements is kept sorted - /// by this field. - pub(crate) member_region_scc: ConstraintSccIndex, - - /// The "best option" that `apply_member_constraint` found -- this was - /// added as an "ad-hoc" lower-bound to `member_region_scc`. - pub(crate) min_choice: ty::RegionVid, - - /// The "member constraint index" -- we can find out details about - /// the constraint from - /// `set.member_constraints[member_constraint_index]`. - pub(crate) member_constraint_index: NllMemberConstraintIndex, -} - #[derive(Debug)] pub(crate) struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential @@ -343,7 +300,6 @@ enum Trace<'a, 'tcx> { StartRegion, FromGraph(&'a OutlivesConstraint<'tcx>), FromStatic(RegionVid), - FromMember(RegionVid, RegionVid, Span), NotVisited, } @@ -453,7 +409,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { placeholder_index_to_region: _, liveness_constraints, mut outlives_constraints, - mut member_constraints, universe_causes, type_tests, } = constraints; @@ -467,7 +422,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all // outlives bounds that we may end up checking. outlives_constraints = Default::default(); - member_constraints = Default::default(); // Also taint the entire scope. infcx.set_tainted_by_errors(guar); @@ -492,9 +446,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_values.merge_liveness(scc, region, &liveness_constraints); } - let member_constraints = - Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r))); - let mut result = Self { definitions, liveness_constraints, @@ -502,9 +453,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint_graph, constraint_sccs, scc_annotations, - rev_scc_graph: None, - member_constraints, - member_constraints_applied: Vec::new(), universe_causes, scc_values, type_tests, @@ -662,19 +610,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_universe(self.constraint_sccs.scc(r)) } - /// Once region solving has completed, this function will return the member constraints that - /// were applied to the value of a given SCC `scc`. See `AppliedMemberConstraint`. - pub(crate) fn applied_member_constraints( - &self, - scc: ConstraintSccIndex, - ) -> &[AppliedMemberConstraint] { - binary_search_util::binary_search_slice( - &self.member_constraints_applied, - |applied| applied.member_region_scc, - &scc, - ) - } - /// Performs region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. @@ -719,12 +654,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!(?errors_buffer); - if errors_buffer.is_empty() { - self.check_member_constraints(infcx, &mut errors_buffer); - } - - debug!(?errors_buffer); - let outlives_requirements = outlives_requirements.unwrap_or_default(); if outlives_requirements.is_empty() { @@ -743,160 +672,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// satisfied. Note that some values may grow **too** large to be /// feasible, but we check this later. #[instrument(skip(self), level = "debug")] - fn propagate_constraints(&mut self) { - debug!("constraints={:#?}", { - let mut constraints: Vec<_> = self.outlives_constraints().collect(); - constraints.sort_by_key(|c| (c.sup, c.sub)); - constraints - .into_iter() - .map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub))) - .collect::>() - }); - + pub(super) fn propagate_constraints(&mut self) { // To propagate constraints, we walk the DAG induced by the // SCC. For each SCC, we visit its successors and compute // their values, then we union all those values to get our // own. - for scc in self.constraint_sccs.all_sccs() { - self.compute_value_for_scc(scc); - } - - // Sort the applied member constraints so we can binary search - // through them later. - self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc); - } - - /// Computes the value of the SCC `scc_a`, which has not yet been - /// computed, by unioning the values of its successors. - /// Assumes that all successors have been computed already - /// (which is assured by iterating over SCCs in dependency order). - #[instrument(skip(self), level = "debug")] - fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) { - // Walk each SCC `B` such that `A: B`... - for &scc_b in self.constraint_sccs.successors(scc_a) { - debug!(?scc_b); - self.scc_values.add_region(scc_a, scc_b); - } - - // Now take member constraints into account. - let member_constraints = Rc::clone(&self.member_constraints); - for m_c_i in member_constraints.indices(scc_a) { - self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i)); - } - - debug!(value = ?self.scc_values.region_value_str(scc_a)); - } - - /// Invoked for each `R0 member of [R1..Rn]` constraint. - /// - /// `scc` is the SCC containing R0, and `choice_regions` are the - /// `R1..Rn` regions -- they are always known to be universal - /// regions (and if that's not true, we just don't attempt to - /// enforce the constraint). - /// - /// The current value of `scc` at the time the method is invoked - /// is considered a *lower bound*. If possible, we will modify - /// the constraint to set it equal to one of the option regions. - /// If we make any changes, returns true, else false. - /// - /// This function only adds the member constraints to the region graph, - /// it does not check them. They are later checked in - /// `check_member_constraints` after the region graph has been computed. - #[instrument(skip(self, member_constraint_index), level = "debug")] - fn apply_member_constraint( - &mut self, - scc: ConstraintSccIndex, - member_constraint_index: NllMemberConstraintIndex, - choice_regions: &[ty::RegionVid], - ) { - // Lazily compute the reverse graph, we'll need it later. - self.compute_reverse_scc_graph(); - - // Create a mutable vector of the options. We'll try to winnow - // them down. - let mut choice_regions: Vec = choice_regions.to_vec(); - - // Convert to the SCC representative: sometimes we have inference - // variables in the member constraint that wind up equated with - // universal regions. The scc representative is the minimal numbered - // one from the corresponding scc so it will be the universal region - // if one exists. - for c_r in &mut choice_regions { - let scc = self.constraint_sccs.scc(*c_r); - *c_r = self.scc_representative(scc); - } - - // If the member region lives in a higher universe, we currently choose - // the most conservative option by leaving it unchanged. - if !self.scc_universe(scc).is_root() { - return; - } - - // The existing value for `scc` is a lower-bound. This will - // consist of some set `{P} + {LB}` of points `{P}` and - // lower-bound free regions `{LB}`. As each choice region `O` - // is a free region, it will outlive the points. But we can - // only consider the option `O` if `O: LB`. - choice_regions.retain(|&o_r| { - self.scc_values - .universal_regions_outlived_by(scc) - .all(|lb| self.universal_region_relations.outlives(o_r, lb)) - }); - debug!(?choice_regions, "after lb"); - - // Now find all the *upper bounds* -- that is, each UB is a - // free region that must outlive the member region `R0` (`UB: - // R0`). Therefore, we need only keep an option `O` if `UB: O` - // for all UB. - let universal_region_relations = &self.universal_region_relations; - for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { - debug!(?ub); - choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); - } - debug!(?choice_regions, "after ub"); - - // At this point we can pick any member of `choice_regions` and would like to choose - // it to be a small as possible. To avoid potential non-determinism we will pick the - // smallest such choice. - // - // Because universal regions are only partially ordered (i.e, not every two regions are - // comparable), we will ignore any region that doesn't compare to all others when picking - // the minimum choice. - // - // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where - // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`. - // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`. - let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| { - choice_regions.iter().all(|&r2| { - self.universal_region_relations.outlives(r1, r2) - || self.universal_region_relations.outlives(r2, r1) - }) - }); - // Now we're left with `['static, 'c]`. Pick `'c` as the minimum! - let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| { - let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); - let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); - match (r1_outlives_r2, r2_outlives_r1) { - (true, true) => r1.min(r2), - (true, false) => r2, - (false, true) => r1, - (false, false) => bug!("incomparable regions in total order"), + for scc_a in self.constraint_sccs.all_sccs() { + debug!(?scc_a); + // Walk each SCC `B` such that `A: B`... + for &scc_b in self.constraint_sccs.successors(scc_a) { + debug!(?scc_b); + self.scc_values.add_region(scc_a, scc_b); } - }) else { - debug!("no unique minimum choice"); - return; - }; - - // As we require `'scc: 'min_choice`, we have definitely already computed - // its `scc_values` at this point. - let min_choice_scc = self.constraint_sccs.scc(min_choice); - debug!(?min_choice, ?min_choice_scc); - if self.scc_values.add_region(scc, min_choice_scc) { - self.member_constraints_applied.push(AppliedMemberConstraint { - member_region_scc: scc, - min_choice, - member_constraint_index, - }); } } @@ -1138,7 +925,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid { debug!("{}", self.region_value_str(r)); - // Find the smallest universal region that contains all other // universal regions within `region`. let mut lub = self.universal_regions().fr_fn_body; @@ -1677,43 +1463,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - #[instrument(level = "debug", skip(self, infcx, errors_buffer))] - fn check_member_constraints( - &self, - infcx: &InferCtxt<'tcx>, - errors_buffer: &mut RegionErrors<'tcx>, - ) { - let member_constraints = Rc::clone(&self.member_constraints); - for m_c_i in member_constraints.all_indices() { - debug!(?m_c_i); - let m_c = &member_constraints[m_c_i]; - let member_region_vid = m_c.member_region_vid; - debug!( - ?member_region_vid, - value = ?self.region_value_str(member_region_vid), - ); - let choice_regions = member_constraints.choice_regions(m_c_i); - debug!(?choice_regions); - - // Did the member region wind up equal to any of the option regions? - if let Some(o) = - choice_regions.iter().find(|&&o_r| self.eval_equal(o_r, m_c.member_region_vid)) - { - debug!("evaluated as equal to {:?}", o); - continue; - } - - // If not, report an error. - let member_region = ty::Region::new_var(infcx.tcx, member_region_vid); - errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion { - span: m_c.definition_span, - hidden_ty: m_c.hidden_ty, - key: m_c.key, - member_region, - }); - } - } - /// We have a constraint `fr1: fr2` that is not satisfied, where /// `fr2` represents some universal region. Here, `r` is some /// region where we know that `fr1: r` and this function has the @@ -1837,20 +1586,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { result.push(c); } - Trace::FromMember(sup, sub, span) => { - let c = OutlivesConstraint { - sup, - sub, - locations: Locations::All(span), - span, - category: ConstraintCategory::OpaqueType, - variance_info: ty::VarianceDiagInfo::default(), - from_closure: false, - }; - p = c.sup; - result.push(c); - } - Trace::StartRegion => { result.reverse(); return Some((result, r)); @@ -1897,15 +1632,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { handle_trace(constraint.sub, Trace::FromGraph(constraint)); } } - - // Member constraints can also give rise to `'r: 'x` edges that - // were not part of the graph initially, so watch out for those. - // (But they are extremely rare; this loop is very cold.) - for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) { - let sub = constraint.min_choice; - let p_c = &self.member_constraints[constraint.member_constraint_index]; - handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span)); - } } None @@ -2232,11 +1958,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self.constraint_sccs } - /// Access to the region graph, built from the outlives constraints. - pub(crate) fn region_graph(&self) -> RegionGraph<'_, 'tcx, graph::Normal> { - self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static) - } - /// Returns the representative `RegionVid` for a given SCC. /// See `RegionTracker` for how a region variable ID is chosen. /// @@ -2269,7 +1990,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } impl<'tcx> RegionDefinition<'tcx> { - fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self { + pub(crate) fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self { // Create a new region definition. Note that, for free // regions, the `external_name` field gets updated later in // `init_free_and_bound_regions`. diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 25cbd579ea1cd..f44ff1fe5672c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,234 +1,659 @@ -use rustc_data_structures::fx::FxIndexMap; +use std::iter; +use std::rc::Rc; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_index::IndexVec; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; +use rustc_infer::traits::ObligationCause; use rustc_macros::extension; +use rustc_middle::bug; +use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{ - self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, fold_regions, + self, DefiningScopeKind, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, + Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, fold_regions, +}; +use rustc_mir_dataflow::points::DenseLocationMap; +use rustc_span::{ErrorGuaranteed, Span}; +use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic; +use rustc_trait_selection::opaque_types::{ + InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid, }; -use rustc_span::Span; -use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid; -use tracing::{debug, instrument}; +use rustc_trait_selection::solve::NoSolution; +use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; +use tracing::{debug, debug_span, instrument}; -use super::RegionInferenceContext; -use crate::BorrowCheckRootCtxt; -use crate::session_diagnostics::LifetimeMismatchOpaqueParam; -use crate::universal_regions::RegionClassification; +use super::reverse_sccs::ReverseSccGraph; +use super::values::RegionValues; +use super::{ConstraintSccs, RegionDefinition, RegionInferenceContext, RegionTracker}; +use crate::constraints::ConstraintSccIndex; +use crate::type_check::canonical::fully_perform_op_raw; +use crate::type_check::free_region_relations::UniversalRegionRelations; +use crate::type_check::{Locations, MirTypeckRegionConstraints}; +use crate::universal_regions::UniversalRegions; +use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, BorrowckState}; -impl<'tcx> RegionInferenceContext<'tcx> { - /// Resolve any opaque types that were encountered while borrow checking - /// this item. This is then used to get the type in the `type_of` query. - /// - /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`. - /// This is lowered to give HIR something like - /// - /// type f<'a>::_Return<'_x> = impl Sized + '_x; - /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x } - /// - /// When checking the return type record the type from the return and the - /// type used in the return value. In this case they might be `_Return<'1>` - /// and `&'2 i32` respectively. - /// - /// Once we to this method, we have completed region inference and want to - /// call `infer_opaque_definition_from_instantiation` to get the inferred - /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation` - /// compares lifetimes directly, so we need to map the inference variables - /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`. - /// - /// First we map the regions in the generic parameters `_Return<'1>` to - /// their `external_name` giving `_Return<'a>`. This step is a bit involved. - /// See the [rustc-dev-guide chapter] for more info. - /// - /// Then we map all the lifetimes in the concrete type to an equal - /// universal region that occurs in the opaque type's args, in this case - /// this would result in `&'a i32`. We only consider regions in the args - /// in case there is an equal region that does not. For example, this should - /// be allowed: - /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }` - /// - /// This will then allow `infer_opaque_definition_from_instantiation` to - /// determine that `_Return<'_x> = &'_x i32`. - /// - /// There's a slight complication around closures. Given - /// `fn f<'a: 'a>() { || {} }` the closure's type is something like - /// `f::<'a>::{{closure}}`. The region parameter from f is essentially - /// ignored by type checking so ends up being inferred to an empty region. - /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`, - /// which has no `external_name` in which case we use `'{erased}` as the - /// region to pass to `infer_opaque_definition_from_instantiation`. +pub(crate) enum DeferredOpaqueTypeError<'tcx> { + UnexpectedHiddenRegion { + /// The opaque type. + opaque_type_key: OpaqueTypeKey<'tcx>, + /// The hidden type containing the member region. + hidden_type: OpaqueHiddenType<'tcx>, + /// The unexpected region. + member_region: Region<'tcx>, + }, + InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>), +} + +impl<'tcx> BorrowCheckRootCtxt<'tcx> { + pub(crate) fn handle_opaque_type_uses(&mut self, borrowck_state: &mut BorrowckState<'tcx>) { + let BorrowckState { + infcx, + body_owned, + universal_region_relations, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + location_map, + deferred_opaque_type_errors, + .. + } = borrowck_state; + + handle_opaque_type_uses( + self, + infcx, + body_owned, + universal_region_relations, + region_bound_pairs, + known_type_outlives_obligations, + location_map, + constraints, + deferred_opaque_type_errors, + ) + } +} + +struct RegionCtxt<'a, 'tcx> { + infcx: &'a BorrowckInferCtxt<'tcx>, + definitions: IndexVec>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + constraint_sccs: ConstraintSccs, + annotations: IndexVec, + rev_scc_graph: ReverseSccGraph, + scc_values: RegionValues, +} + +impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { + /// Creates a new `RegionCtxt` used to compute defining opaque type uses. /// - /// [rustc-dev-guide chapter]: - /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html - #[instrument(level = "debug", skip(self, root_cx, infcx), ret)] - pub(crate) fn infer_opaque_types( - &self, - root_cx: &mut BorrowCheckRootCtxt<'tcx>, - infcx: &InferCtxt<'tcx>, - opaque_ty_decls: FxIndexMap, OpaqueHiddenType<'tcx>>, - ) { - let mut decls_modulo_regions: FxIndexMap, (OpaqueTypeKey<'tcx>, Span)> = - FxIndexMap::default(); - - for (opaque_type_key, concrete_type) in opaque_ty_decls { - debug!(?opaque_type_key, ?concrete_type); - - let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> = - vec![(self.universal_regions().fr_static, infcx.tcx.lifetimes.re_static)]; - - let opaque_type_key = - opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| { - // Use the SCC representative instead of directly using `region`. - // See [rustc-dev-guide chapter] § "Strict lifetime equality". - let scc = self.constraint_sccs.scc(region.as_var()); - let vid = self.scc_representative(scc); - let named = match self.definitions[vid].origin { - // Iterate over all universal regions in a consistent order and find the - // *first* equal region. This makes sure that equal lifetimes will have - // the same name and simplifies subsequent handling. - // See [rustc-dev-guide chapter] § "Semantic lifetime equality". - NllRegionVariableOrigin::FreeRegion => self - .universal_regions() - .universal_regions_iter() - .filter(|&ur| { - // See [rustc-dev-guide chapter] § "Closure restrictions". - !matches!( - self.universal_regions().region_classification(ur), - Some(RegionClassification::External) - ) - }) - .find(|&ur| self.universal_region_relations.equal(vid, ur)) - .map(|ur| self.definitions[ur].external_name.unwrap()), - NllRegionVariableOrigin::Placeholder(placeholder) => { - Some(ty::Region::new_placeholder(infcx.tcx, placeholder)) - } - NllRegionVariableOrigin::Existential { .. } => None, - } - .unwrap_or_else(|| { - ty::Region::new_error_with_message( - infcx.tcx, - concrete_type.span, - "opaque type with non-universal region args", - ) - }); + /// This does not yet propagate region values. This is instead done lazily + /// when applying member constraints. + fn new( + infcx: &'a BorrowckInferCtxt<'tcx>, + universal_region_relations: &'a UniversalRegionRelations<'tcx>, + location_map: Rc, + constraints: &MirTypeckRegionConstraints<'tcx>, + ) -> RegionCtxt<'a, 'tcx> { + let mut definitions: IndexVec<_, _> = infcx + .get_region_var_infos() + .iter() + .map(|info| RegionDefinition::new(info.universe, info.origin)) + .collect(); - arg_regions.push((vid, named)); - named - }); - debug!(?opaque_type_key, ?arg_regions); - - let concrete_type = fold_regions(infcx.tcx, concrete_type, |region, _| { - arg_regions - .iter() - .find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid)) - .map(|&(_, arg_named)| arg_named) - .unwrap_or(infcx.tcx.lifetimes.re_erased) - }); - debug!(?concrete_type); - - let ty = - infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type); - - // Sometimes, when the hidden type is an inference variable, it can happen that - // the hidden type becomes the opaque type itself. In this case, this was an opaque - // usage of the opaque type and we can ignore it. This check is mirrored in typeck's - // writeback. - if !infcx.next_trait_solver() { - if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() - && alias_ty.def_id == opaque_type_key.def_id.to_def_id() - && alias_ty.args == opaque_type_key.args - { - continue; + for (external_name, variable) in + universal_region_relations.universal_regions.named_universal_regions_iter() + { + definitions[variable].external_name = Some(external_name); + } + + let universal_regions = &universal_region_relations.universal_regions; + let fr_static = universal_regions.fr_static; + let (constraint_sccs, annotations) = + constraints.outlives_constraints.compute_sccs(fr_static, &definitions); + let rev_scc_graph = ReverseSccGraph::compute(&constraint_sccs, universal_regions); + // Unlike the `RegionInferenceContext`, we only care about free regions + // and fully ignore liveness and placeholders. + let placeholder_indices = Default::default(); + let mut scc_values = + RegionValues::new(location_map, universal_regions.len(), placeholder_indices); + for variable in definitions.indices() { + let scc = constraint_sccs.scc(variable); + match definitions[variable].origin { + NllRegionVariableOrigin::FreeRegion => { + scc_values.add_element(scc, variable); } + _ => {} } + } - root_cx.add_concrete_opaque_type( - opaque_type_key.def_id, - OpaqueHiddenType { span: concrete_type.span, ty }, - ); - - // Check that all opaque types have the same region parameters if they have the same - // non-region parameters. This is necessary because within the new solver we perform - // various query operations modulo regions, and thus could unsoundly select some impls - // that don't hold. - if !ty.references_error() - && let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( - infcx.tcx.erase_regions(opaque_type_key), - (opaque_type_key, concrete_type.span), - ) - && let Some((arg1, arg2)) = std::iter::zip( - prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - ) - .find(|(arg1, arg2)| arg1 != arg2) - { - infcx.dcx().emit_err(LifetimeMismatchOpaqueParam { - arg: arg1, - prev: arg2, - span: prev_span, - prev_span: concrete_type.span, - }); - } + RegionCtxt { + infcx, + definitions, + universal_region_relations, + constraint_sccs, + annotations, + rev_scc_graph, + scc_values, } } - /// Map the regions in the type to named regions. This is similar to what - /// `infer_opaque_types` does, but can infer any universal region, not only - /// ones from the args for the opaque type. It also doesn't double check - /// that the regions produced are in fact equal to the named region they are - /// replaced with. This is fine because this function is only to improve the - /// region names in error messages. - /// - /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly - /// lax with mapping region vids that are *shorter* than a universal region to - /// that universal region. This is useful for member region constraints since - /// we want to suggest a universal region name to capture even if it's technically - /// not equal to the error region. - pub(crate) fn name_regions_for_member_constraint(&self, tcx: TyCtxt<'tcx>, ty: T) -> T - where - T: TypeFoldable>, + fn representative(&self, vid: RegionVid) -> RegionVid { + let scc = self.constraint_sccs.scc(vid); + self.annotations[scc].representative + } + + fn universal_regions(&self) -> &UniversalRegions<'tcx> { + &self.universal_region_relations.universal_regions + } + + fn eval_equal(&self, r1_vid: RegionVid, r2_vid: RegionVid) -> bool { + let r1 = self.constraint_sccs.scc(r1_vid); + let r2 = self.constraint_sccs.scc(r2_vid); + + if r1 == r2 { + return true; + } + + let universal_outlives = |sub, sup| { + self.scc_values.universal_regions_outlived_by(sub).all(|r1| { + self.scc_values + .universal_regions_outlived_by(sup) + .any(|r2| self.universal_region_relations.outlives(r2, r1)) + }) + }; + universal_outlives(r1, r2) && universal_outlives(r2, r1) + } +} + +pub(crate) fn handle_opaque_type_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + location_map: &Rc, + constraints: &mut MirTypeckRegionConstraints<'tcx>, + deferred_errors: &mut Vec>, +) { + let tcx = infcx.tcx; + let opaque_types = infcx.clone_opaque_types(); + if opaque_types.is_empty() { + return; + } + + let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); + // We need to eagerly map all regions to NLL vars here, as we need to make sure we've + // introduced nll vars for all used placeholders. + let mut opaque_types = opaque_types + .into_iter() + .map(|entry| { + fold_regions(tcx, infcx.resolve_vars_if_possible(entry), |r, _| { + let vid = if let ty::RePlaceholder(placeholder) = r.kind() { + constraints.placeholder_region(infcx, placeholder).as_var() + } else { + universal_region_relations.universal_regions.to_region_vid(r) + }; + Region::new_var(tcx, vid) + }) + }) + .collect::>(); + + debug!(?opaque_types); + + collect_defining_uses( + root_cx, + infcx, + constraints, + deferred_errors, + universal_region_relations, + location_map, + &mut opaque_types, + ); + + apply_defining_uses( + root_cx, + infcx, + body, + &universal_region_relations.universal_regions, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + &opaque_types, + ); + + if let Some((key, hidden_type)) = infcx + .inner + .borrow_mut() + .opaque_types() + .opaque_types_added_since(opaque_types_storage_num_entries) + .next() { - fold_regions(tcx, ty, |region, _| match region.kind() { + let opaque_type_string = tcx.def_path_str(key.def_id); + let msg = format!("unexpected cyclic definition of `{opaque_type_string}`"); + infcx.dcx().span_delayed_bug(hidden_type.span, msg); + } + + let _ = infcx.take_opaque_types(); +} + +#[derive(Debug)] +struct DefiningUse<'tcx> { + opaque_type_key: OpaqueTypeKey<'tcx>, + arg_regions: Vec, + hidden_type: OpaqueHiddenType<'tcx>, +} + +fn collect_defining_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, + deferred_errors: &mut Vec>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + location_map: &Rc, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], +) { + let tcx = infcx.tcx; + let mut rcx = + RegionCtxt::new(infcx, universal_region_relations, Rc::clone(location_map), constraints); + + let mut defining_uses = vec![]; + for &(opaque_type_key, hidden_type) in opaque_types { + let non_nll_opaque_type_key = + opaque_type_key.fold_captured_lifetime_args(rcx.infcx.tcx, |r| { + let vid = rcx.representative(r.as_var()); + rcx.definitions[vid].external_name.unwrap_or(r) + }); + if let Err(_) = check_opaque_type_parameter_valid( + infcx, + non_nll_opaque_type_key, + hidden_type.span, + DefiningScopeKind::MirBorrowck, + ) { + debug!(?non_nll_opaque_type_key, "not a defining use"); + continue; + } + + let arg_regions = iter::once(rcx.universal_regions().fr_static) + .chain( + opaque_type_key + .iter_captured_args(tcx) + .filter_map(|(_, arg)| arg.as_region()) + .map(Region::as_var), + ) + .collect(); + defining_uses.push(DefiningUse { opaque_type_key, arg_regions, hidden_type }); + } + + debug!(?defining_uses); + + apply_member_constraints(&mut rcx, &defining_uses); + map_defining_uses_to_definition_site(root_cx, &rcx, &defining_uses, deferred_errors); +} + +fn apply_member_constraints<'tcx>( + rcx: &mut RegionCtxt<'_, 'tcx>, + defining_uses: &[DefiningUse<'tcx>], +) { + // Start by collecting the member constraints of all defining uses. + // + // Applying member constraints can influence other member constraints, + // so we first collect and then apply them. + let mut member_constraints = Default::default(); + for defining_use in defining_uses { + let mut visitor = CollectMemberConstraintsVisitor { + rcx, + defining_use, + member_constraints: &mut member_constraints, + }; + defining_use.hidden_type.ty.visit_with(&mut visitor); + } + + debug!(?member_constraints); + for scc_a in rcx.constraint_sccs.all_sccs() { + debug!(?scc_a); + for &scc_b in rcx.constraint_sccs.successors(scc_a) { + debug!(?scc_b); + rcx.scc_values.add_region(scc_a, scc_b); + } + + for defining_use in member_constraints.get(&scc_a).into_iter().flatten() { + apply_member_constraint(rcx, scc_a, &defining_use.arg_regions); + } + } +} + +#[instrument(level = "debug", skip(rcx))] +fn apply_member_constraint<'tcx>( + rcx: &mut RegionCtxt<'_, 'tcx>, + member: ConstraintSccIndex, + arg_regions: &[RegionVid], +) { + // The existing value of `'member` is a lower-bound. If its is already larger than + // some universal region, we cannot equate it with that region. Said differently, we + // ignore choice regions which are smaller than this member region. + let mut choice_regions = arg_regions + .iter() + .copied() + .map(|r| rcx.representative(r)) + .filter(|&choice_region| { + rcx.scc_values.universal_regions_outlived_by(member).all(|lower_bound| { + rcx.universal_region_relations.outlives(choice_region, lower_bound) + }) + }) + .collect::>(); + debug!(?choice_regions, "after enforcing lower-bound"); + + // Now find all the *upper bounds* -- that is, each UB is a + // free region that must outlive the member region `R0` (`UB: + // R0`). Therefore, we need only keep an option `O` if `UB: O` + // for all UB. + // + // If we have a requirement `'upper_bound: 'member`, equating `'member` + // with some region `'choice` means we now also require `'upper_bound: 'choice`. + // Avoid choice regions for which this does not hold. + for ub in rcx.rev_scc_graph.upper_bounds(member) { + choice_regions + .retain(|&choice_region| rcx.universal_region_relations.outlives(ub, choice_region)); + } + debug!(?choice_regions, "after enforcing upper-bound"); + + // At this point we can pick any member of `choice_regions` and would like to choose + // it to be a small as possible. To avoid potential non-determinism we will pick the + // smallest such choice. + // + // Because universal regions are only partially ordered (i.e, not every two regions are + // comparable), we will ignore any region that doesn't compare to all others when picking + // the minimum choice. + // + // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where + // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`. + // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`. + let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| { + choice_regions.iter().all(|&r2| { + rcx.universal_region_relations.outlives(r1, r2) + || rcx.universal_region_relations.outlives(r2, r1) + }) + }); + // Now we're left with `['static, 'c]`. Pick `'c` as the minimum! + let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| { + let r1_outlives_r2 = rcx.universal_region_relations.outlives(r1, r2); + let r2_outlives_r1 = rcx.universal_region_relations.outlives(r2, r1); + match (r1_outlives_r2, r2_outlives_r1) { + (true, true) => r1.min(r2), + (true, false) => r2, + (false, true) => r1, + (false, false) => bug!("incomparable regions in total order"), + } + }) else { + debug!("no unique minimum choice"); + return; + }; + + debug!(?min_choice); + let min_choice_scc = rcx.constraint_sccs.scc(min_choice); + rcx.scc_values.add_region(member, min_choice_scc); +} + +struct CollectMemberConstraintsVisitor<'a, 'b, 'tcx> { + rcx: &'a RegionCtxt<'a, 'tcx>, + defining_use: &'b DefiningUse<'tcx>, + member_constraints: &'a mut FxHashMap>>, +} +impl<'tcx> CollectMemberConstraintsVisitor<'_, '_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.rcx.infcx.tcx + } + fn visit_closure_args(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) { + let generics = self.cx().generics_of(def_id); + for arg in args.iter().skip(generics.parent_count) { + arg.visit_with(self); + } + } +} +impl<'tcx> TypeVisitor> for CollectMemberConstraintsVisitor<'_, '_, 'tcx> { + // Invoked for each member region in the hidden type. If possible, we try to lift + // it to one of the `arg_regions` from the `opaque_type_key`. + // + // If there's a unique minimum choice, we emit a `'member: 'min_choice` constraint. + // Note that we do not require the two regions to be equal... TODO examples + fn visit_region(&mut self, r: Region<'tcx>) { + match r.kind() { + ty::ReBound(..) => return, ty::ReVar(vid) => { - let scc = self.constraint_sccs.scc(vid); + let scc = self.rcx.constraint_sccs.scc(vid); + self.member_constraints.entry(scc).or_default().push(self.defining_use); + } + _ => unreachable!(), + } + } - // Special handling of higher-ranked regions. - if !self.scc_universe(scc).is_root() { - match self.scc_values.placeholders_contained_in(scc).enumerate().last() { - // If the region contains a single placeholder then they're equal. - Some((0, placeholder)) => { - return ty::Region::new_placeholder(tcx, placeholder); - } + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return; + } - // Fallback: this will produce a cryptic error message. - _ => return region, + match *ty.kind() { + ty::Closure(def_id, args) + | ty::CoroutineClosure(def_id, args) + | ty::Coroutine(def_id, args) => self.visit_closure_args(def_id, args), + + ty::Alias(kind, ty::AliasTy { def_id, args, .. }) + if let Some(variances) = self.cx().opt_alias_variances(kind, def_id) => + { + // Skip lifetime parameters that are not captured, since they do + // not need member constraints registered for them; we'll erase + // them (and hopefully in the future replace them with placeholders). + for (&v, arg) in std::iter::zip(variances, args.iter()) { + if v != ty::Bivariant { + arg.visit_with(self) } } + } - // Find something that we can name - let upper_bound = self.approx_universal_upper_bound(vid); - if let Some(universal_region) = self.definitions[upper_bound].external_name { - return universal_region; - } + _ => ty.super_visit_with(self), + } + } +} - // Nothing exact found, so we pick a named upper bound, if there's only one. - // If there's >1 universal region, then we probably are dealing w/ an intersection - // region which cannot be mapped back to a universal. - // FIXME: We could probably compute the LUB if there is one. - let scc = self.constraint_sccs.scc(vid); - let upper_bounds: Vec<_> = self - .rev_scc_graph - .as_ref() - .unwrap() - .upper_bounds(scc) - .filter_map(|vid| self.definitions[vid].external_name) - .filter(|r| !r.is_static()) - .collect(); - match &upper_bounds[..] { - [universal_region] => *universal_region, - _ => region, - } +fn map_defining_uses_to_definition_site<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + rcx: &RegionCtxt<'_, 'tcx>, + defining_uses: &[DefiningUse<'tcx>], + deferred_errors: &mut Vec>, +) { + for &DefiningUse { opaque_type_key, ref arg_regions, hidden_type } in defining_uses { + let opaque_type_key = opaque_type_key.fold_captured_lifetime_args(rcx.infcx.tcx, |r| { + let vid = rcx.representative(r.as_var()); + rcx.definitions[vid].external_name.unwrap() + }); + let _span = debug_span!( + "map_defining_uses_to_definition_site", + ?opaque_type_key, + ?arg_regions, + ?hidden_type + ); + // TODO: explain what's going on here + let mut visitor = + HiddenTypeMeh { rcx, arg_regions, opaque_type_key, hidden_type, deferred_errors }; + let hidden_type = hidden_type.fold_with(&mut visitor); + let ty = rcx + .infcx + .infer_opaque_definition_from_instantiation(opaque_type_key, hidden_type) + .unwrap_or_else(|err| { + deferred_errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err)); + Ty::new_error_with_message( + rcx.infcx.tcx, + hidden_type.span, + "deferred invalid opaque type args", + ) + }); + root_cx.add_concrete_opaque_type( + opaque_type_key.def_id, + OpaqueHiddenType { span: hidden_type.span, ty }, + ); + } +} +struct HiddenTypeMeh<'a, 'tcx> { + rcx: &'a RegionCtxt<'a, 'tcx>, + arg_regions: &'a [RegionVid], + + opaque_type_key: OpaqueTypeKey<'tcx>, + hidden_type: OpaqueHiddenType<'tcx>, + deferred_errors: &'a mut Vec>, +} + +impl<'tcx> HiddenTypeMeh<'_, 'tcx> { + fn fold_closure_args( + &mut self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> GenericArgsRef<'tcx> { + let generics = self.cx().generics_of(def_id); + self.cx().mk_args_from_iter(args.iter().enumerate().map(|(index, arg)| { + if index < generics.parent_count { + // We're not using `tcx.erase_regions` as that also anonymizes bound variables, + // causing type mismatches. + fold_regions(self.cx(), arg, |_, _| self.cx().lifetimes.re_erased) + } else { + arg.fold_with(self) } - _ => region, - }) + })) + } +} +impl<'tcx> TypeFolder> for HiddenTypeMeh<'_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.rcx.infcx.tcx + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r.kind() { + // ignore bound regions, keep visiting + ty::ReBound(_, _) => r, + _ => self + .arg_regions + .iter() + .copied() + .find(|&arg_vid| self.rcx.eval_equal(r.as_var(), arg_vid)) + .map(|r| { + let vid = self.rcx.representative(r); + self.rcx.definitions[vid].external_name.unwrap() + }) + .unwrap_or_else(|| { + self.deferred_errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion { + hidden_type: self.hidden_type, + opaque_type_key: self.opaque_type_key, + member_region: r, + }); + ty::Region::new_error_with_message( + self.cx(), + self.hidden_type.span, + "opaque type with non-universal region args", + ) + }), + } + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return ty; + } + + let tcx = self.cx(); + match *ty.kind() { + ty::Closure(def_id, args) => { + Ty::new_closure(tcx, def_id, self.fold_closure_args(def_id, args)) + } + + ty::CoroutineClosure(def_id, args) => { + Ty::new_coroutine_closure(tcx, def_id, self.fold_closure_args(def_id, args)) + } + + ty::Coroutine(def_id, args) => { + Ty::new_coroutine(tcx, def_id, self.fold_closure_args(def_id, args)) + } + + ty::Alias(kind, ty::AliasTy { def_id, args, .. }) + if let Some(variances) = tcx.opt_alias_variances(kind, def_id) => + { + // Skip lifetime parameters that are not captured, since they do + // not need member constraints registered for them; we'll erase + // them (and hopefully in the future replace them with placeholders). + let args = + tcx.mk_args_from_iter(std::iter::zip(variances, args.iter()).map(|(&v, s)| { + if v == ty::Bivariant { + // We're not using `tcx.erase_regions` as that also anonymizes bound variables, + // causing type mismatches. + fold_regions(self.cx(), s, |_, _| self.cx().lifetimes.re_erased) + } else { + s.fold_with(self) + } + })); + ty::AliasTy::new_from_args(tcx, def_id, args).to_ty(tcx) + } + + _ => ty.super_fold_with(self), + } + } +} + +fn apply_defining_uses<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + constraints: &mut MirTypeckRegionConstraints<'tcx>, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], +) { + let tcx = infcx.tcx; + for &(key, hidden_type) in opaque_types { + let Some(expected) = root_cx.get_concrete_opaque_type(key.def_id) else { + let guar = + tcx.dcx().span_err(hidden_type.span, "non-defining use in the defining scope"); + root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + infcx.set_tainted_by_errors(guar); + continue; + }; + + let expected = ty::fold_regions(tcx, expected.instantiate(tcx, key.args), |re, _dbi| { + match re.kind() { + ty::ReErased => infcx.next_nll_region_var( + NllRegionVariableOrigin::Existential { from_forall: false }, + || crate::RegionCtxt::Existential(None), + ), + _ => re, + } + }); + + let locations = Locations::All(hidden_type.span); + if let Err(guar) = fully_perform_op_raw( + infcx, + body, + universal_regions, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + locations, + ConstraintCategory::OpaqueType, + CustomTypeOp::new( + |ocx| { + let cause = ObligationCause::misc( + hidden_type.span, + body.source.def_id().expect_local(), + ); + let actual_ty = ocx.normalize(&cause, infcx.param_env, hidden_type.ty); + let expected_ty = ocx.normalize(&cause, infcx.param_env, expected.ty); + ocx.eq(&cause, infcx.param_env, actual_ty, expected_ty).map_err(|_| NoSolution) + }, + "equating opaque types", + ), + ) { + root_cx.add_concrete_opaque_type(key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + } } } @@ -262,19 +687,16 @@ impl<'tcx> InferCtxt<'tcx> { &self, opaque_type_key: OpaqueTypeKey<'tcx>, instantiated_ty: OpaqueHiddenType<'tcx>, - ) -> Ty<'tcx> { - if let Some(e) = self.tainted_by_errors() { - return Ty::new_error(self.tcx, e); + ) -> Result, InvalidOpaqueTypeArgs<'tcx>> { + if let Some(guar) = self.tainted_by_errors() { + return Err(guar.into()); } - - if let Err(err) = check_opaque_type_parameter_valid( + check_opaque_type_parameter_valid( self, opaque_type_key, instantiated_ty.span, DefiningScopeKind::MirBorrowck, - ) { - return Ty::new_error(self.tcx, err.report(self)); - } + )?; let definition_ty = instantiated_ty .remap_generic_params_to_declaration_params( @@ -284,10 +706,131 @@ impl<'tcx> InferCtxt<'tcx> { ) .ty; - if let Err(e) = definition_ty.error_reported() { - return Ty::new_error(self.tcx, e); + definition_ty.error_reported()?; + Ok(definition_ty) + } +} + +impl<'tcx> RegionInferenceContext<'tcx> { + pub(crate) fn emit_deferred_opaque_type_errors( + &self, + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + errors: Vec>, + ) { + let mut prev_hidden_region_errors = FxHashMap::default(); + let mut guar = None; + for error in errors { + guar = Some(match error { + DeferredOpaqueTypeError::UnexpectedHiddenRegion { + opaque_type_key, + hidden_type, + member_region, + } => self.report_unexpected_hidden_region_errors( + root_cx, + infcx, + &mut prev_hidden_region_errors, + opaque_type_key, + hidden_type, + member_region, + ), + DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx), + }); } + let guar = guar.unwrap(); + root_cx.set_tainted_by_errors(guar); + infcx.set_tainted_by_errors(guar); + } + + fn report_unexpected_hidden_region_errors( + &self, + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, + prev_errors: &mut FxHashMap<(Span, Ty<'tcx>, OpaqueTypeKey<'tcx>), ErrorGuaranteed>, + opaque_type_key: OpaqueTypeKey<'tcx>, + hidden_type: OpaqueHiddenType<'tcx>, + member_region: Region<'tcx>, + ) -> ErrorGuaranteed { + let tcx = infcx.tcx; + let named_ty = self.name_regions_for_member_constraint(tcx, hidden_type.ty); + let named_key = self.name_regions_for_member_constraint(tcx, opaque_type_key); + let named_region = self.name_regions_for_member_constraint(tcx, member_region); + + *prev_errors.entry((hidden_type.span, named_ty, named_key)).or_insert_with(|| { + let guar = unexpected_hidden_region_diagnostic( + infcx, + root_cx.root_def_id(), + hidden_type.span, + named_ty, + named_region, + named_key, + ) + .emit(); + root_cx + .add_concrete_opaque_type(named_key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + guar + }) + } + + /// Map the regions in the type to named regions. This is similar to what + /// `infer_opaque_types` does, but can infer any universal region, not only + /// ones from the args for the opaque type. It also doesn't double check + /// that the regions produced are in fact equal to the named region they are + /// replaced with. This is fine because this function is only to improve the + /// region names in error messages. + /// + /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly + /// lax with mapping region vids that are *shorter* than a universal region to + /// that universal region. This is useful for member region constraints since + /// we want to suggest a universal region name to capture even if it's technically + /// not equal to the error region. + fn name_regions_for_member_constraint(&self, tcx: TyCtxt<'tcx>, ty: T) -> T + where + T: TypeFoldable>, + { + fold_regions(tcx, ty, |region, _| match region.kind() { + ty::ReVar(vid) => { + let scc = self.constraint_sccs.scc(vid); + + // Special handling of higher-ranked regions. + if !self.scc_universe(scc).is_root() { + match self.scc_values.placeholders_contained_in(scc).enumerate().last() { + // If the region contains a single placeholder then they're equal. + Some((0, placeholder)) => { + return ty::Region::new_placeholder(tcx, placeholder); + } + + // Fallback: this will produce a cryptic error message. + _ => return region, + } + } + + // Find something that we can name + let upper_bound = self.approx_universal_upper_bound(vid); + if let Some(universal_region) = self.definitions[upper_bound].external_name { + return universal_region; + } - definition_ty + // Nothing exact found, so we pick a named upper bound, if there's only one. + // If there's >1 universal region, then we probably are dealing w/ an intersection + // region which cannot be mapped back to a universal. + // FIXME: We could probably compute the LUB if there is one. + let scc = self.constraint_sccs.scc(vid); + let rev_scc_graph = &ReverseSccGraph::compute( + &self.constraint_sccs, + &self.universal_region_relations.universal_regions, + ); + let upper_bounds: Vec<_> = rev_scc_graph + .upper_bounds(scc) + .filter_map(|vid| self.definitions[vid].external_name) + .filter(|r| !r.is_static()) + .collect(); + match &upper_bounds[..] { + [universal_region] => *universal_region, + _ => region, + } + } + _ => region, + }) } } diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index 8e04791461b26..e8da85eccef16 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -5,7 +5,6 @@ use rustc_data_structures::graph; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_middle::ty::RegionVid; -use crate::RegionInferenceContext; use crate::constraints::ConstraintSccIndex; use crate::region_infer::ConstraintSccs; use crate::universal_regions::UniversalRegions; @@ -57,15 +56,3 @@ impl ReverseSccGraph { .filter(move |r| duplicates.insert(*r)) } } - -impl RegionInferenceContext<'_> { - /// Compute the reverse SCC-based constraint graph (lazily). - pub(super) fn compute_reverse_scc_graph(&mut self) { - if self.rev_scc_graph.is_some() { - return; - } - - self.rev_scc_graph = - Some(ReverseSccGraph::compute(&self.constraint_sccs, self.universal_regions())); - } -} diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index f1427218cdb02..f856e3b49d72a 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -37,6 +37,7 @@ pub(crate) enum RegionElement { /// Records the CFG locations where each region is live. When we initially compute liveness, we use /// an interval matrix storing liveness ranges for each region-vid. +#[derive(Clone)] pub(crate) struct LivenessValues { /// The map from locations to points. location_map: Rc, @@ -193,7 +194,7 @@ impl LivenessValues { /// Maps from `ty::PlaceholderRegion` values that are used in the rest of /// rustc to the internal `PlaceholderIndex` values that are used in /// NLL. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub(crate) struct PlaceholderIndices { indices: FxIndexSet, } diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 66b526fa02a50..db82f5fd52938 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -2,11 +2,17 @@ use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; -use rustc_middle::ty::{OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{EarlyBinder, OpaqueHiddenType, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; use smallvec::SmallVec; -use crate::{ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults}; +use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions}; +use crate::nll::compute_closure_requirements_modulo_opaques; +use crate::type_check::apply_closure_requirements_considering_opaques; +use crate::{ + BorrowckState, ClosureRegionRequirements, ConcreteOpaqueTypes, PropagatedBorrowCheckResults, + resume_do_mir_borrowck, start_do_mir_borrowck, +}; /// The shared context used by both the root as well as all its nested /// items. @@ -14,7 +20,10 @@ pub(super) struct BorrowCheckRootCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, root_def_id: LocalDefId, concrete_opaque_types: ConcreteOpaqueTypes<'tcx>, - nested_bodies: FxHashMap>, + partial_results: FxHashMap>>, + closure_requirements_modulo_opaques: + FxHashMap>>, + final_results: FxHashMap>, tainted_by_errors: Option, } @@ -24,11 +33,17 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { tcx, root_def_id, concrete_opaque_types: Default::default(), - nested_bodies: Default::default(), + partial_results: Default::default(), + closure_requirements_modulo_opaques: Default::default(), + final_results: Default::default(), tainted_by_errors: None, } } + pub(super) fn root_def_id(&self) -> LocalDefId { + self.root_def_id + } + /// Collect all defining uses of opaque types inside of this typeck root. This /// expects the hidden type to be mapped to the definition parameters of the opaque /// and errors if we end up with distinct hidden types. @@ -58,47 +73,144 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { } } + pub(super) fn get_concrete_opaque_type( + &mut self, + def_id: LocalDefId, + ) -> Option>> { + self.concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) + } + pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) { self.tainted_by_errors = Some(guar); } - pub(super) fn get_or_insert_nested( - &mut self, - def_id: LocalDefId, - ) -> &PropagatedBorrowCheckResults<'tcx> { + fn compute_partial_results(&mut self, def_id: LocalDefId) { + debug_assert_eq!( + self.tcx.typeck_root_def_id(def_id.to_def_id()), + self.root_def_id.to_def_id() + ); + if !self.partial_results.contains_key(&def_id) { + let result = start_do_mir_borrowck(self, def_id, None); + // We only need to store the partial result if it depends on opaque types + // or depends on a nested item which does. + let relies_on_opaques = result.infcx.has_opaque_types_in_storage() + || !result.deferred_closure_requirements.is_empty(); + + let to_insert = if !relies_on_opaques { + let final_result = resume_do_mir_borrowck(self, None, result).0; + if self.final_results.insert(def_id, final_result).is_some() { + bug!("unexpected previous final result for {def_id:?}"); + } + None + } else { + Some(result) + }; + + if self.partial_results.insert(def_id, to_insert).is_some() { + bug!("unexpected previous partial result for: {def_id:?}") + } + } + } + + fn get_or_insert_final(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> { debug_assert_eq!( self.tcx.typeck_root_def_id(def_id.to_def_id()), self.root_def_id.to_def_id() ); - if !self.nested_bodies.contains_key(&def_id) { - let result = super::do_mir_borrowck(self, def_id, None).0; - if let Some(prev) = self.nested_bodies.insert(def_id, result) { + if !self.final_results.contains_key(&def_id) { + let mut yield_do_mir_borrowck = self.partial_results.remove(&def_id).unwrap().unwrap(); + self.handle_opaque_type_uses(&mut yield_do_mir_borrowck); + apply_closure_requirements_considering_opaques(self, &mut yield_do_mir_borrowck); + let result = resume_do_mir_borrowck(self, None, yield_do_mir_borrowck).0; + if let Some(prev) = self.final_results.insert(def_id, result) { bug!("unexpected previous nested body: {prev:?}"); } } - self.nested_bodies.get(&def_id).unwrap() + self.final_results.get(&def_id).unwrap() } - pub(super) fn closure_requirements( + pub(crate) fn get_closure_requirements_considering_regions( &mut self, - nested_body_def_id: LocalDefId, + def_id: LocalDefId, ) -> &Option> { - &self.get_or_insert_nested(nested_body_def_id).closure_requirements + &self.get_or_insert_final(def_id).closure_requirements + } + + /// Get the closure requirements of the nested body `def_id`. In case + /// this nested body relies on opaques, checking that the hidden type + /// matches the final definition may introduce new region constraints + /// we aren't considering yet. + /// + /// In these cases, we need to later add these requirements again, including + /// the constraints from opaque types. + pub(crate) fn get_closure_requirements_modulo_opaques( + &mut self, + def_id: LocalDefId, + ) -> (&Option>, bool) { + self.compute_partial_results(def_id); + // In case the nested item does not use any opaque types or depend on nested items + // which do, we eagerly compute its final result to avoid duplicate work. + if let Some(final_result) = self.final_results.get(&def_id) { + (&final_result.closure_requirements, false) + } else if self.closure_requirements_modulo_opaques.contains_key(&def_id) { + (self.closure_requirements_modulo_opaques.get(&def_id).unwrap(), true) + } else { + let partial_result = self.partial_results.get(&def_id).unwrap().as_ref().unwrap(); + let modulo_opaques = compute_closure_requirements_modulo_opaques(partial_result); + self.closure_requirements_modulo_opaques.insert(def_id, modulo_opaques); + (self.closure_requirements_modulo_opaques.get(&def_id).unwrap(), true) + } } pub(super) fn used_mut_upvars( &mut self, nested_body_def_id: LocalDefId, ) -> &SmallVec<[FieldIdx; 8]> { - &self.get_or_insert_nested(nested_body_def_id).used_mut_upvars + &self.get_or_insert_final(nested_body_def_id).used_mut_upvars } - pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { - if let Some(guar) = self.tainted_by_errors { + /// The actual borrowck routine. This should only be called for the typeck root, + /// not for any nested bodies. + pub(super) fn borrowck_root( + mut self, + consumer_options: Option, + ) -> ( + Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed>, + Option>>, + ) { + let root_def_id = self.root_def_id; + let mut yield_do_mir_borrowck = + start_do_mir_borrowck(&mut self, root_def_id, consumer_options); + + self.handle_opaque_type_uses(&mut yield_do_mir_borrowck); + apply_closure_requirements_considering_opaques(&mut self, &mut yield_do_mir_borrowck); + let (PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars }, consumer_data) = + resume_do_mir_borrowck(&mut self, consumer_options, yield_do_mir_borrowck); + + // We need to manually borrowck all nested bodies from the HIR as + // we do not generate MIR for dead code. Not doing so causes us to + // never check closures in dead code. + let nested_bodies = self.tcx.nested_bodies_within(root_def_id); + for def_id in nested_bodies { + if !self.final_results.contains_key(&def_id) { + self.compute_partial_results(def_id); + let _ = self.get_or_insert_final(def_id); + } + } + + #[allow(rustc::potential_query_instability)] + if cfg!(debug_assertions) { + assert!(closure_requirements.is_none()); + assert!(used_mut_upvars.is_empty()); + assert!(self.partial_results.values().all(|entry| entry.is_none())); + } + + let result = if let Some(guar) = self.tainted_by_errors { Err(guar) } else { - Ok(self.tcx.arena.alloc(self.concrete_opaque_types)) - } + Ok(&*self.tcx.arena.alloc(self.concrete_opaque_types)) + }; + (result, consumer_data) } } diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 5143b2fa20598..652822fa7c892 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -1,7 +1,7 @@ use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::{GenericArg, Ty}; +use rustc_middle::ty::Ty; use rustc_span::Span; use crate::diagnostics::RegionName; @@ -294,19 +294,6 @@ pub(crate) struct MoveBorrow<'a> { pub borrow_span: Span, } -#[derive(Diagnostic)] -#[diag(borrowck_opaque_type_lifetime_mismatch)] -pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> { - pub arg: GenericArg<'tcx>, - pub prev: GenericArg<'tcx>, - #[primary_span] - #[label] - #[note] - pub span: Span, - #[label(borrowck_prev_lifetime_label)] - pub prev_span: Span, -} - #[derive(Subdiagnostic)] pub(crate) enum CaptureReasonLabel<'a> { #[label(borrowck_moved_due_to_call)] diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index b3fa786a5177c..906a07ee33bb3 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -2,8 +2,9 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_middle::bug; -use rustc_middle::mir::ConstraintCategory; +use rustc_middle::mir::{Body, ConstraintCategory}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -13,8 +14,70 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; use tracing::{debug, instrument}; -use super::{Locations, NormalizeLocation, TypeChecker}; +use super::{Locations, MirTypeckRegionConstraints, NormalizeLocation, TypeChecker}; +use crate::BorrowckInferCtxt; use crate::diagnostics::ToUniverseInfo; +use crate::type_check::constraint_conversion; +use crate::universal_regions::UniversalRegions; + +#[instrument(skip(infcx, constraints, op), level = "trace")] +pub(crate) fn fully_perform_op_raw<'tcx, R: fmt::Debug, Op>( + infcx: &BorrowckInferCtxt<'tcx>, + body: &Body<'tcx>, + universal_regions: &UniversalRegions<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + constraints: &mut MirTypeckRegionConstraints<'tcx>, + locations: Locations, + category: ConstraintCategory<'tcx>, + op: Op, +) -> Result +where + Op: type_op::TypeOp<'tcx, Output = R>, + Op::ErrorInfo: ToUniverseInfo<'tcx>, +{ + let old_universe = infcx.universe(); + + let TypeOpOutput { output, constraints: query_constraints, error_info } = + op.fully_perform(infcx, locations.span(body))?; + if cfg!(debug_assertions) { + let data = infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + panic!("leftover region constraints: {data:#?}"); + } + } + + debug!(?output, ?query_constraints); + + if let Some(data) = query_constraints { + constraint_conversion::ConstraintConversion::new( + infcx, + universal_regions, + region_bound_pairs, + infcx.param_env, + known_type_outlives_obligations, + locations, + locations.span(body), + category, + constraints, + ) + .convert_all(data); + } + + // If the query has created new universes and errors are going to be emitted, register the + // cause of these new universes for improved diagnostics. + let universe = infcx.universe(); + if old_universe != universe + && let Some(error_info) = error_info + { + let universe_info = error_info.to_universe_info(old_universe); + for u in (old_universe + 1)..=universe { + constraints.universe_causes.insert(u, universe_info.clone()); + } + } + + Ok(output) +} impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// Given some operation `op` that manipulates types, proves @@ -27,7 +90,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { /// **Any `rustc_infer::infer` operations that might generate region /// constraints should occur within this method so that those /// constraints can be properly localized!** - #[instrument(skip(self, op), level = "trace")] pub(super) fn fully_perform_op( &mut self, locations: Locations, @@ -38,36 +100,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Op: type_op::TypeOp<'tcx, Output = R>, Op::ErrorInfo: ToUniverseInfo<'tcx>, { - let old_universe = self.infcx.universe(); - - let TypeOpOutput { output, constraints, error_info } = - op.fully_perform(self.infcx, locations.span(self.body))?; - if cfg!(debug_assertions) { - let data = self.infcx.take_and_reset_region_constraints(); - if !data.is_empty() { - panic!("leftover region constraints: {data:#?}"); - } - } - - debug!(?output, ?constraints); - - if let Some(data) = constraints { - self.push_region_constraints(locations, category, data); - } - - // If the query has created new universes and errors are going to be emitted, register the - // cause of these new universes for improved diagnostics. - let universe = self.infcx.universe(); - if old_universe != universe - && let Some(error_info) = error_info - { - let universe_info = error_info.to_universe_info(old_universe); - for u in (old_universe + 1)..=universe { - self.constraints.universe_causes.insert(u, universe_info.clone()); - } - } - - Ok(output) + fully_perform_op_raw( + self.infcx, + self.body, + self.universal_regions, + self.region_bound_pairs, + self.known_type_outlives_obligations, + self.constraints, + locations, + category, + op, + ) } pub(super) fn instantiate_canonical( diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 92732aba29ba3..c28cd8456f532 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -17,7 +17,7 @@ use type_op::TypeOpOutput; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; use crate::universal_regions::UniversalRegions; -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct UniversalRegionRelations<'tcx> { pub(crate) universal_regions: UniversalRegions<'tcx>, @@ -156,13 +156,6 @@ impl UniversalRegionRelations<'_> { self.outlives.contains(fr1, fr2) } - /// Returns `true` if fr1 is known to equal fr2. - /// - /// This will only ever be true for universally quantified regions. - pub(crate) fn equal(&self, fr1: RegionVid, fr2: RegionVid) -> bool { - self.outlives.contains(fr1, fr2) && self.outlives.contains(fr2, fr1) - } - /// Returns a vector of free regions `x` such that `fr1: x` is /// known to hold. pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c51225712093..9d143e14ef74a 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -26,8 +26,8 @@ use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::{ self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt, - Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, - TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions, + Dynamic, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, + fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::ResultsCursor; @@ -44,7 +44,6 @@ use tracing::{debug, instrument, trace}; use crate::borrow_set::BorrowSet; use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::diagnostics::UniverseInfo; -use crate::member_constraints::MemberConstraintSet; use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; use crate::polonius::{PoloniusContext, PoloniusLivenessContext}; use crate::region_infer::TypeTest; @@ -52,7 +51,9 @@ use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderI use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations}; use crate::universal_regions::{DefiningTy, UniversalRegions}; -use crate::{BorrowCheckRootCtxt, BorrowckInferCtxt, path_utils}; +use crate::{ + BorrowCheckRootCtxt, BorrowckInferCtxt, BorrowckState, DeferredClosureRequirements, path_utils, +}; macro_rules! span_mirbug { ($context:expr, $elem:expr, $($message:tt)*) => ({ @@ -69,12 +70,11 @@ macro_rules! span_mirbug { }) } -mod canonical; +pub(crate) mod canonical; mod constraint_conversion; pub(crate) mod free_region_relations; mod input_output; pub(crate) mod liveness; -mod opaque_types; mod relate_tys; /// Type checks the given `mir` in the context of the inference @@ -118,7 +118,6 @@ pub(crate) fn type_check<'a, 'tcx>( placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)), outlives_constraints: OutlivesConstraintSet::default(), - member_constraints: MemberConstraintSet::default(), type_tests: Vec::default(), universe_causes: FxIndexMap::default(), }; @@ -159,6 +158,7 @@ pub(crate) fn type_check<'a, 'tcx>( polonius_facts, borrow_set, constraints: &mut constraints, + deferred_closure_requirements: Default::default(), polonius_liveness, }; @@ -169,10 +169,10 @@ pub(crate) fn type_check<'a, 'tcx>( liveness::generate(&mut typeck, &location_map, flow_inits, move_data); - let opaque_type_values = - opaque_types::take_opaques_and_register_member_constraints(&mut typeck); - // We're done with typeck, we can finalize the polonius liveness context for region inference. + // + // FIXME: Handling opaque type uses may introduce new regions. This likely has to be moved to + // a later point. let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| { PoloniusContext::create_from_liveness( liveness_context, @@ -181,10 +181,13 @@ pub(crate) fn type_check<'a, 'tcx>( ) }); + let deferred_closure_requirements = typeck.deferred_closure_requirements; MirTypeckResults { constraints, universal_region_relations, - opaque_type_values, + region_bound_pairs, + known_type_outlives_obligations, + deferred_closure_requirements, polonius_context, } } @@ -224,6 +227,7 @@ struct TypeChecker<'a, 'tcx> { polonius_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, + deferred_closure_requirements: DeferredClosureRequirements<'tcx>, /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. polonius_liveness: Option, } @@ -233,12 +237,15 @@ struct TypeChecker<'a, 'tcx> { pub(crate) struct MirTypeckResults<'tcx> { pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, pub(crate) universal_region_relations: Frozen>, - pub(crate) opaque_type_values: FxIndexMap, OpaqueHiddenType<'tcx>>, + pub(crate) region_bound_pairs: Frozen>, + pub(crate) known_type_outlives_obligations: Frozen>>, + pub(crate) deferred_closure_requirements: DeferredClosureRequirements<'tcx>, pub(crate) polonius_context: Option, } /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. +#[derive(Clone)] pub(crate) struct MirTypeckRegionConstraints<'tcx> { /// Maps from a `ty::Placeholder` to the corresponding /// `PlaceholderIndex` bit that we will use for it. @@ -265,8 +272,6 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>, - pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>, - pub(crate) universe_causes: FxIndexMap>, pub(crate) type_tests: Vec>, @@ -275,7 +280,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { impl<'tcx> MirTypeckRegionConstraints<'tcx> { /// Creates a `Region` for a given `PlaceholderRegion`, or returns the /// region that corresponds to a previously created one. - fn placeholder_region( + pub(crate) fn placeholder_region( &mut self, infcx: &InferCtxt<'tcx>, placeholder: ty::PlaceholderRegion, @@ -368,14 +373,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.body } - fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> RegionVid { - if let ty::RePlaceholder(placeholder) = r.kind() { - self.constraints.placeholder_region(self.infcx, placeholder).as_var() - } else { - self.universal_regions.to_region_vid(r) - } - } - fn unsized_feature_enabled(&self) -> bool { let features = self.tcx().features(); features.unsized_locals() || features.unsized_fn_params() @@ -2502,7 +2499,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { args: GenericArgsRef<'tcx>, locations: Locations, ) -> ty::InstantiatedPredicates<'tcx> { - if let Some(closure_requirements) = &self.root_cx.closure_requirements(def_id) { + let (closure_requirements, needs_defer) = + self.root_cx.get_closure_requirements_modulo_opaques(def_id); + if needs_defer { + self.deferred_closure_requirements.push((def_id, args, locations)); + } + + if let Some(closure_requirements) = closure_requirements { constraint_conversion::ConstraintConversion::new( self.infcx, self.universal_regions, @@ -2556,6 +2559,41 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } +pub(crate) fn apply_closure_requirements_considering_opaques<'tcx>( + root_cx: &mut BorrowCheckRootCtxt<'tcx>, + borrowck_state: &mut BorrowckState<'tcx>, +) { + let BorrowckState { + infcx, + body_owned, + universal_region_relations, + region_bound_pairs, + known_type_outlives_obligations, + constraints, + deferred_closure_requirements, + .. + } = borrowck_state; + + for (def_id, args, locations) in mem::take(deferred_closure_requirements).into_iter() { + if let Some(closure_requirements) = + root_cx.get_closure_requirements_considering_regions(def_id) + { + constraint_conversion::ConstraintConversion::new( + infcx, + &universal_region_relations.universal_regions, + region_bound_pairs, + infcx.param_env, + known_type_outlives_obligations, + locations, + body_owned.span, // irrelevant; will be overridden. + ConstraintCategory::Boring, // same as above. + constraints, + ) + .apply_closure_requirements(closure_requirements, def_id, args); + } + } +} + trait NormalizeLocation: fmt::Debug + Copy { fn to_locations(self) -> Locations; } diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs deleted file mode 100644 index 341c50c37f6db..0000000000000 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ /dev/null @@ -1,333 +0,0 @@ -use std::iter; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_middle::span_bug; -use rustc_middle::ty::{ - self, GenericArgKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, fold_regions, -}; -use tracing::{debug, trace}; - -use super::{MemberConstraintSet, TypeChecker}; - -/// Once we're done with typechecking the body, we take all the opaque types -/// defined by this function and add their 'member constraints'. -pub(super) fn take_opaques_and_register_member_constraints<'tcx>( - typeck: &mut TypeChecker<'_, 'tcx>, -) -> FxIndexMap, OpaqueHiddenType<'tcx>> { - let infcx = typeck.infcx; - // Annoying: to invoke `typeck.to_region_vid`, we need access to - // `typeck.constraints`, but we also want to be mutating - // `typeck.member_constraints`. For now, just swap out the value - // we want and replace at the end. - let mut member_constraints = std::mem::take(&mut typeck.constraints.member_constraints); - let opaque_types = infcx - .take_opaque_types() - .into_iter() - .map(|(opaque_type_key, hidden_type)| { - let hidden_type = infcx.resolve_vars_if_possible(hidden_type); - register_member_constraints( - typeck, - &mut member_constraints, - opaque_type_key, - hidden_type, - ); - trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind()); - if hidden_type.has_non_region_infer() { - span_bug!(hidden_type.span, "could not resolve {:?}", hidden_type.ty); - } - - // Convert all regions to nll vars. - let (opaque_type_key, hidden_type) = - fold_regions(infcx.tcx, (opaque_type_key, hidden_type), |r, _| { - ty::Region::new_var(infcx.tcx, typeck.to_region_vid(r)) - }); - - (opaque_type_key, hidden_type) - }) - .collect(); - assert!(typeck.constraints.member_constraints.is_empty()); - typeck.constraints.member_constraints = member_constraints; - opaque_types -} - -/// Given the map `opaque_types` containing the opaque -/// `impl Trait` types whose underlying, hidden types are being -/// inferred, this method adds constraints to the regions -/// appearing in those underlying hidden types to ensure that they -/// at least do not refer to random scopes within the current -/// function. These constraints are not (quite) sufficient to -/// guarantee that the regions are actually legal values; that -/// final condition is imposed after region inference is done. -/// -/// # The Problem -/// -/// Let's work through an example to explain how it works. Assume -/// the current function is as follows: -/// -/// ```text -/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) -/// ``` -/// -/// Here, we have two `impl Trait` types whose values are being -/// inferred (the `impl Bar<'a>` and the `impl -/// Bar<'b>`). Conceptually, this is sugar for a setup where we -/// define underlying opaque types (`Foo1`, `Foo2`) and then, in -/// the return type of `foo`, we *reference* those definitions: -/// -/// ```text -/// type Foo1<'x> = impl Bar<'x>; -/// type Foo2<'x> = impl Bar<'x>; -/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } -/// // ^^^^ ^^ -/// // | | -/// // | args -/// // def_id -/// ``` -/// -/// As indicating in the comments above, each of those references -/// is (in the compiler) basically generic parameters (`args`) -/// applied to the type of a suitable `def_id` (which identifies -/// `Foo1` or `Foo2`). -/// -/// Now, at this point in compilation, what we have done is to -/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with -/// fresh inference variables C1 and C2. We wish to use the values -/// of these variables to infer the underlying types of `Foo1` and -/// `Foo2`. That is, this gives rise to higher-order (pattern) unification -/// constraints like: -/// -/// ```text -/// for<'a> (Foo1<'a> = C1) -/// for<'b> (Foo1<'b> = C2) -/// ``` -/// -/// For these equation to be satisfiable, the types `C1` and `C2` -/// can only refer to a limited set of regions. For example, `C1` -/// can only refer to `'static` and `'a`, and `C2` can only refer -/// to `'static` and `'b`. The job of this function is to impose that -/// constraint. -/// -/// Up to this point, C1 and C2 are basically just random type -/// inference variables, and hence they may contain arbitrary -/// regions. In fact, it is fairly likely that they do! Consider -/// this possible definition of `foo`: -/// -/// ```text -/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { -/// (&*x, &*y) -/// } -/// ``` -/// -/// Here, the values for the concrete types of the two impl -/// traits will include inference variables: -/// -/// ```text -/// &'0 i32 -/// &'1 i32 -/// ``` -/// -/// Ordinarily, the subtyping rules would ensure that these are -/// sufficiently large. But since `impl Bar<'a>` isn't a specific -/// type per se, we don't get such constraints by default. This -/// is where this function comes into play. It adds extra -/// constraints to ensure that all the regions which appear in the -/// inferred type are regions that could validly appear. -/// -/// This is actually a bit of a tricky constraint in general. We -/// want to say that each variable (e.g., `'0`) can only take on -/// values that were supplied as arguments to the opaque type -/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in -/// scope. We don't have a constraint quite of this kind in the current -/// region checker. -/// -/// # The Solution -/// -/// We generally prefer to make `<=` constraints, since they -/// integrate best into the region solver. To do that, we find the -/// "minimum" of all the arguments that appear in the args: that -/// is, some region which is less than all the others. In the case -/// of `Foo1<'a>`, that would be `'a` (it's the only choice, after -/// all). Then we apply that as a least bound to the variables -/// (e.g., `'a <= '0`). -/// -/// In some cases, there is no minimum. Consider this example: -/// -/// ```text -/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } -/// ``` -/// -/// Here we would report a more complex "in constraint", like `'r -/// in ['a, 'b, 'static]` (where `'r` is some region appearing in -/// the hidden type). -/// -/// # Constrain regions, not the hidden concrete type -/// -/// Note that generating constraints on each region `Rc` is *not* -/// the same as generating an outlives constraint on `Tc` itself. -/// For example, if we had a function like this: -/// -/// ``` -/// # #![feature(type_alias_impl_trait)] -/// # fn main() {} -/// # trait Foo<'a> {} -/// # impl<'a, T> Foo<'a> for (&'a u32, T) {} -/// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { -/// (x, y) -/// } -/// -/// // Equivalent to: -/// # mod dummy { use super::*; -/// type FooReturn<'a, T> = impl Foo<'a>; -/// #[define_opaque(FooReturn)] -/// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { -/// (x, y) -/// } -/// # } -/// ``` -/// -/// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` -/// is an inference variable). If we generated a constraint that -/// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- -/// but this is not necessary, because the opaque type we -/// create will be allowed to reference `T`. So we only generate a -/// constraint that `'0: 'a`. -fn register_member_constraints<'tcx>( - typeck: &mut TypeChecker<'_, 'tcx>, - member_constraints: &mut MemberConstraintSet<'tcx, ty::RegionVid>, - opaque_type_key: OpaqueTypeKey<'tcx>, - OpaqueHiddenType { span, ty: hidden_ty }: OpaqueHiddenType<'tcx>, -) { - let tcx = typeck.tcx(); - let hidden_ty = typeck.infcx.resolve_vars_if_possible(hidden_ty); - debug!(?hidden_ty); - - let variances = tcx.variances_of(opaque_type_key.def_id); - debug!(?variances); - - // For a case like `impl Foo<'a, 'b>`, we would generate a constraint - // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the - // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`). - // - // `conflict1` and `conflict2` are the two region bounds that we - // detected which were unrelated. They are used for diagnostics. - - // Create the set of choice regions: each region in the hidden - // type can be equal to any of the region parameters of the - // opaque type definition. - let fr_static = typeck.universal_regions.fr_static; - let choice_regions: Vec<_> = opaque_type_key - .args - .iter() - .enumerate() - .filter(|(i, _)| variances[*i] == ty::Invariant) - .filter_map(|(_, arg)| match arg.unpack() { - GenericArgKind::Lifetime(r) => Some(typeck.to_region_vid(r)), - GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, - }) - .chain(iter::once(fr_static)) - .collect(); - - // FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's - // not currently sound until we have existential regions. - hidden_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx, - op: |r| { - member_constraints.add_member_constraint( - opaque_type_key, - hidden_ty, - span, - typeck.to_region_vid(r), - &choice_regions, - ) - }, - }); -} - -/// Visitor that requires that (almost) all regions in the type visited outlive -/// `least_region`. We cannot use `push_outlives_components` because regions in -/// closure signatures are not included in their outlives components. We need to -/// ensure all regions outlive the given bound so that we don't end up with, -/// say, `ReVar` appearing in a return type and causing ICEs when other -/// functions end up with region constraints involving regions from other -/// functions. -/// -/// We also cannot use `for_each_free_region` because for closures it includes -/// the regions parameters from the enclosing item. -/// -/// We ignore any type parameters because impl trait values are assumed to -/// capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> { - tcx: TyCtxt<'tcx>, - op: OP, -} - -impl<'tcx, OP> TypeVisitor> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - fn visit_region(&mut self, r: ty::Region<'tcx>) { - match r.kind() { - // ignore bound regions, keep visiting - ty::ReBound(_, _) => {} - _ => (self.op)(r), - } - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) { - // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { - return; - } - - match *ty.kind() { - ty::Closure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_closure().upvar_tys() { - upvar.visit_with(self); - } - args.as_closure().sig_as_fn_ptr_ty().visit_with(self); - } - - ty::CoroutineClosure(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - - for upvar in args.as_coroutine_closure().upvar_tys() { - upvar.visit_with(self); - } - - args.as_coroutine_closure().signature_parts_ty().visit_with(self); - } - - ty::Coroutine(_, args) => { - // Skip lifetime parameters of the enclosing item(s) - // Also skip the witness type, because that has no free regions. - - for upvar in args.as_coroutine().upvar_tys() { - upvar.visit_with(self); - } - args.as_coroutine().return_ty().visit_with(self); - args.as_coroutine().yield_ty().visit_with(self); - args.as_coroutine().resume_ty().visit_with(self); - } - - ty::Alias(kind, ty::AliasTy { def_id, args, .. }) - if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) => - { - // Skip lifetime parameters that are not captured, since they do - // not need member constraints registered for them; we'll erase - // them (and hopefully in the future replace them with placeholders). - for (v, s) in std::iter::zip(variances, args.iter()) { - if *v != ty::Bivariant { - s.visit_with(self); - } - } - } - - _ => { - ty.super_visit_with(self); - } - } - } -} diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index c11e14d214c42..43081c2d96825 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -39,7 +39,7 @@ use tracing::{debug, instrument}; use crate::BorrowckInferCtxt; use crate::renumber::RegionCtxt; -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct UniversalRegions<'tcx> { indices: UniversalRegionIndices<'tcx>, @@ -199,7 +199,7 @@ impl<'tcx> DefiningTy<'tcx> { } } -#[derive(Debug)] +#[derive(Clone, Debug)] struct UniversalRegionIndices<'tcx> { /// For those regions that may appear in the parameter environment /// ('static and early-bound regions), we maintain a map from the diff --git a/compiler/rustc_data_structures/src/frozen.rs b/compiler/rustc_data_structures/src/frozen.rs index 73190574667f3..5678686ab991b 100644 --- a/compiler/rustc_data_structures/src/frozen.rs +++ b/compiler/rustc_data_structures/src/frozen.rs @@ -46,7 +46,7 @@ //! Frozen::freeze(new_bar)`). /// An owned immutable value. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Frozen(T); impl Frozen { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b408d76010d7b..810cc5e8db446 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -944,6 +944,10 @@ impl<'tcx> InferCtxt<'tcx> { storage.var_infos.clone() } + pub fn has_opaque_types_in_storage(&self) -> bool { + !self.inner.borrow().opaque_type_storage.is_empty() + } + #[instrument(level = "debug", skip(self), ret)] pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)> { self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0759fa3da428a..e835a9c943a50 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3340,11 +3340,6 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.opts.unstable_opts.next_solver.coherence } - #[allow(rustc::bad_opt_access)] - pub fn use_typing_mode_borrowck(self) -> bool { - self.next_trait_solver_globally() || self.sess.opts.unstable_opts.typing_mode_borrowck - } - pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { self.opt_rpitit_info(def_id).is_some() } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b2a58897c31bd..38ce2ec3c7f18 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -885,12 +885,12 @@ impl<'tcx> OpaqueHiddenType<'tcx> { // // We erase regions when doing this during HIR typeck. let this = match defining_scope_kind { - DefiningScopeKind::HirTypeck => tcx.erase_regions(self), + DefiningScopeKind::HirTypeck => fold_regions(tcx, self, |_, _| tcx.lifetimes.re_erased), DefiningScopeKind::MirBorrowck => self, }; let result = this.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span)); if cfg!(debug_assertions) && matches!(defining_scope_kind, DefiningScopeKind::HirTypeck) { - assert_eq!(result.ty, tcx.erase_regions(result.ty)); + assert_eq!(result.ty, fold_regions(tcx, result.ty, |_, _| tcx.lifetimes.re_erased)); } result } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 3d9fdcbc7b14c..5447358a9e4c1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2569,9 +2569,6 @@ written to standard error output)"), "in diagnostics, use heuristics to shorten paths referring to items"), tune_cpu: Option = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), - #[rustc_lint_opt_deny_field_access("use `TyCtxt::use_typing_mode_borrowck` instead of this field")] - typing_mode_borrowck: bool = (false, parse_bool, [TRACKED], - "enable `TypingMode::Borrowck`, changing the way opaque types are handled during MIR borrowck"), #[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")] ub_checks: Option = (None, parse_opt_bool, [TRACKED], "emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"), diff --git a/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs b/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs new file mode 100644 index 0000000000000..c8e0edc906bf5 --- /dev/null +++ b/tests/ui/nll/member-constraints/non-root-universe-existential-1.rs @@ -0,0 +1,31 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Proj<'a> { + type Assoc; +} + +impl<'a, 'b, F: FnOnce() -> &'b ()> Proj<'a> for F { + type Assoc = (); +} + +fn is_proj Proj<'a>>(f: F) {} + +fn define<'a>() -> impl Sized + use<'a> { + // This defines the RPIT to `&'unconstrained_b ()`, an inference + // variable which is in a higher universe as gets created inside + // of the binder of `F: for<'a> Proj<'a>`. This previously caused + // us to not apply member constraints. + // + // This was unnecessary. It is totally acceptable for member regions + // to be able to name placeholders from higher universes, as long as + // they don't actually do so. + is_proj(define::<'a>); + &() +} + +fn main() {} + + diff --git a/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs b/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs new file mode 100644 index 0000000000000..a35d0ddd5cfa1 --- /dev/null +++ b/tests/ui/nll/member-constraints/non-root-universe-existential-2.rs @@ -0,0 +1,31 @@ +//@ check-pass + +// Unlike `non-root-universe-existential-1.rs` this previously +// compiled. We didn't attempt to define the hidden type of +// `impl Iterator` when projecting through it. We now do. Further +// minimizing this is challenging. + +struct Type(Vec); +enum TypeTreeValueIter<'a, T> { + Once(T), + Ref(&'a ()), +} + +impl<'a, T> Iterator for TypeTreeValueIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + loop {} + } +} + +fn item>(x: I) -> ::Item { + loop {} +} + +fn get_type_tree_values<'a>(ty: &'a Type) -> impl Iterator { + let _: &'a Type = item(std::iter::once(ty).map(get_type_tree_values)); + TypeTreeValueIter::<'a, &'a Type>::Once(ty) +} + +fn main() {} From 5d6d183f803475896f68b5b65e6f68cb1b18e548 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 2 May 2025 01:30:45 +0000 Subject: [PATCH 02/17] add tests --- tests/ui/impl-trait/no-anonymize-regions.rs | 20 ++++++++++++ .../nested-impl-trait-pass.rs | 4 ++- .../reject-choice-due-to-prev-constraint.rs | 31 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/ui/impl-trait/no-anonymize-regions.rs create mode 100644 tests/ui/nll/member-constraints/reject-choice-due-to-prev-constraint.rs diff --git a/tests/ui/impl-trait/no-anonymize-regions.rs b/tests/ui/impl-trait/no-anonymize-regions.rs new file mode 100644 index 0000000000000..42529c5adc060 --- /dev/null +++ b/tests/ui/impl-trait/no-anonymize-regions.rs @@ -0,0 +1,20 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// A regression test for an error in `redis` while working on #139587. +// +// We check for structural equality when adding defining uses of opaques. +// In this test one defining use had anonymized regions while the other +// one did not, causing an error. +struct W(T); +fn constrain R, T, R>(f: F) -> R { + loop {} +} +fn foo<'a>(x: for<'b> fn(&'b ())) -> impl Sized + use<'a> { + let mut r = constrain(foo::<'_>); + r = W(x); + r +} +fn main() {} diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs b/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs index 4633ad6823024..7ba6addbc542f 100644 --- a/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs +++ b/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs @@ -8,7 +8,9 @@ impl Cap<'_> for T {} // Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: // - '?15 member ['static, 'a, 'b] // from outer impl-trait // - '?15 member ['static, 'a] // from inner impl-trait -// To satisfy both we can only choose 'a. +// To satisfy both we can only choose 'a. Concretely, the applying the first member constraint +// requires ?15 to outlive at least 'b while the second requires ?15 to outlive 'a. As +// 'a outlives 'b we end up with 'a as the final member region. fn pass_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator> + Cap<'b> where 's: 'a, diff --git a/tests/ui/nll/member-constraints/reject-choice-due-to-prev-constraint.rs b/tests/ui/nll/member-constraints/reject-choice-due-to-prev-constraint.rs new file mode 100644 index 0000000000000..5eb86a934c5b1 --- /dev/null +++ b/tests/ui/nll/member-constraints/reject-choice-due-to-prev-constraint.rs @@ -0,0 +1,31 @@ +//@ check-pass + +// We've got `'0 member ['a, 'b, 'static]` and `'1 member ['a, 'b, 'static]`. +// +// As '0 gets outlived by 'a - its "upper bound" - the only applicable choice +// region is 'a. +// +// '1 has to outlive 'b so the only applicabel choice regions are 'b and 'static. +// Considering this member constraint by itself would choose 'b as it is the +// smaller of the two regions. +// +// However, this is only the case when ignoring the member constraint on '0. +// After applying this constraint and requiring '0 to outlive 'a. As '1 outlives +// '0, the region 'b is no longer an applicable choice region for '1 as 'b does +// does not outlive 'a. We would therefore choose 'static. +// +// This means applying member constraints is order dependent. We handle this by +// first applying member constraints for regions 'x and then consider the resulting +// constraints when applying member constraints for regions 'y with 'y: 'x. +fn with_constraints<'r0, 'r1, 'a, 'b>() -> *mut (&'r0 (), &'r1 ()) +where + 'r1: 'r0, + 'a: 'r0, + 'r1: 'b, +{ + loop {} +} +fn foo<'a, 'b>() -> impl Sized + use<'a, 'b> { + with_constraints::<'_, '_, 'a, 'b>() +} +fn main() {} From 96738e45f70330a51d105f4f2660e6a630efa801 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 30 Apr 2025 01:47:23 +0000 Subject: [PATCH 03/17] support revealing uses in HIR typeck --- compiler/rustc_hir_typeck/src/lib.rs | 4 + compiler/rustc_hir_typeck/src/opaque_types.rs | 122 +++++++++++++++++- compiler/rustc_hir_typeck/src/writeback.rs | 30 +++-- .../src/solve/eval_ctxt/mod.rs | 24 ---- .../src/solve/normalizes_to/opaque_types.rs | 109 +++------------- .../opaques/revealing-use-in-nested-body.rs | 3 + .../opaques/universal-args-non-defining.rs | 16 +++ 7 files changed, 183 insertions(+), 125 deletions(-) create mode 100644 tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 161f5e981d430..abe33fd435b03 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -226,6 +226,10 @@ fn typeck_with_inspect<'tcx>( fcx.select_obligations_where_possible(|_| {}); + if fcx.next_trait_solver() { + fcx.handle_opaque_type_uses_next(); + } + debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); // This must be the last thing before `report_ambiguity_errors`. diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index e0224f8c6e1b4..a4b83a5efec21 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -1,5 +1,123 @@ -use super::FnCtxt; -impl<'tcx> FnCtxt<'_, 'tcx> { +use rustc_middle::ty::{ + DefiningScopeKind, EarlyBinder, OpaqueHiddenType, OpaqueTypeKey, Ty, TypeVisitableExt, +}; +use rustc_trait_selection::opaque_types::{ + InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid, +}; +use tracing::{debug, instrument}; + +use crate::FnCtxt; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + #[instrument(level = "debug", skip(self))] + pub(super) fn handle_opaque_type_uses_next(&mut self) { + // We clone the opaques instead of stealing them here as they are still used for + // normalization in the next generation trait solver. + // + // FIXME(-Znext-solver): Opaque types defined after this would simply get dropped + // at the end of typeck. Ideally we can feed some query here to no longer define + // new opaque uses but instead always reveal by using the definitions inferred here. + let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types(); + let num_entries = self.inner.borrow_mut().opaque_types().num_entries(); + let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries)); + debug_assert_eq!(prev, None); + for entry in &mut opaque_types { + *entry = self.resolve_vars_if_possible(*entry); + } + debug!(?opaque_types); + + self.collect_defining_uses(&opaque_types); + self.apply_defining_uses(&opaque_types); + } + + fn collect_defining_uses( + &mut self, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], + ) { + let tcx = self.tcx; + let typeck_results = &mut *self.typeck_results.borrow_mut(); + for &(opaque_type_key, hidden_type) in opaque_types { + match check_opaque_type_parameter_valid( + &self, + opaque_type_key, + hidden_type.span, + DefiningScopeKind::HirTypeck, + ) { + Ok(()) => {} + Err(InvalidOpaqueTypeArgs::AlreadyReported(guar)) => { + typeck_results + .concrete_opaque_types + .insert(opaque_type_key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + } + // Not a defining use, ignore and treat as revealing use instead. + Err( + InvalidOpaqueTypeArgs::NotAParam { .. } + | InvalidOpaqueTypeArgs::DuplicateParam { .. }, + ) => continue, + } + + // We ignore uses of the opaque if they have any inference variables + // as this can frequently happen with recursive calls. + // + // See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`. + if hidden_type.ty.has_non_region_infer() { + continue; + } + + let hidden_type = hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + tcx, + DefiningScopeKind::HirTypeck, + ); + + if let Some(prev) = + typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type) + { + let entry = + typeck_results.concrete_opaque_types.get_mut(&opaque_type_key.def_id).unwrap(); + if prev.ty != hidden_type.ty { + if let Some(guar) = typeck_results.tainted_by_errors { + entry.ty = Ty::new_error(tcx, guar); + } else { + let (Ok(guar) | Err(guar)) = + prev.build_mismatch_error(&hidden_type, tcx).map(|d| d.emit()); + entry.ty = Ty::new_error(tcx, guar); + } + } + + // Pick a better span if there is one. + // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. + entry.span = prev.span.substitute_dummy(hidden_type.span); + } + } + + // FIXME(-Znext-solver): Check that all opaques have been defined hre. + } + + fn apply_defining_uses( + &mut self, + opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], + ) { + let tcx = self.tcx; + for &(key, hidden_type) in opaque_types { + let Some(&expected) = + self.typeck_results.borrow_mut().concrete_opaque_types.get(&key.def_id) + else { + let guar = + tcx.dcx().span_err(hidden_type.span, "non-defining use in the defining scope"); + self.typeck_results + .borrow_mut() + .concrete_opaque_types + .insert(key.def_id, OpaqueHiddenType::new_error(tcx, guar)); + self.set_tainted_by_errors(guar); + continue; + }; + + let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); + self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); + } + } + /// We may in theory add further uses of an opaque after cloning the opaque /// types storage during writeback when computing the defining uses. /// diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index b2497cb0de16b..5ebbc6d6cc117 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -534,26 +534,34 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_opaque_types_next(&mut self) { + let fcx_typeck_results = self.fcx.typeck_results.borrow(); + assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); + for (&def_id, &hidden_type) in &fcx_typeck_results.concrete_opaque_types { + assert!(!hidden_type.has_infer()); + self.typeck_results.concrete_opaque_types.insert(def_id, hidden_type); + } + } + #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { + if self.fcx.next_trait_solver() { + return self.visit_opaque_types_next(); + } + let tcx = self.tcx(); - // We clone the opaques instead of stealing them here as they are still used for - // normalization in the next generation trait solver. - let opaque_types = self.fcx.infcx.clone_opaque_types(); + let opaque_types = self.fcx.infcx.take_opaque_types(); let num_entries = self.fcx.inner.borrow_mut().opaque_types().num_entries(); let prev = self.fcx.checked_opaque_types_storage_entries.replace(Some(num_entries)); debug_assert_eq!(prev, None); for (opaque_type_key, hidden_type) in opaque_types { let hidden_type = self.resolve(hidden_type, &hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span); - - if !self.fcx.next_trait_solver() { - if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() - && alias_ty.def_id == opaque_type_key.def_id.to_def_id() - && alias_ty.args == opaque_type_key.args - { - continue; - } + if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args + { + continue; } if let Err(err) = check_opaque_type_parameter_valid( diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index fc5dad9a3edf5..d31a82b68cb88 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -4,7 +4,6 @@ use std::ops::ControlFlow; #[cfg(feature = "nightly")] use rustc_macros::HashStable_NoContext; use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; -use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; @@ -1075,29 +1074,6 @@ where self.add_goals(GoalSource::AliasWellFormed, goals); } - // Do something for each opaque/hidden pair defined with `def_id` in the - // current inference context. - pub(super) fn probe_existing_opaque_ty( - &mut self, - key: ty::OpaqueTypeKey, - ) -> Option<(ty::OpaqueTypeKey, I::Ty)> { - // We shouldn't have any duplicate entries when using - // this function during `TypingMode::Analysis`. - let duplicate_entries = self.delegate.clone_duplicate_opaque_types(); - assert!(duplicate_entries.is_empty(), "unexpected duplicates: {duplicate_entries:?}"); - let mut matching = self.delegate.clone_opaque_types_lookup_table().into_iter().filter( - |(candidate_key, _)| { - candidate_key.def_id == key.def_id - && DeepRejectCtxt::relate_rigid_rigid(self.cx()) - .args_may_unify(candidate_key.args, key.args) - }, - ); - let first = matching.next(); - let second = matching.next(); - assert_eq!(second, None); - first - } - // Try to evaluate a const, or return `None` if the const is too generic. // This doesn't mean the const isn't evaluatable, though, and should be treated // as an ambiguity rather than no-solution. diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index df3ad1e468bb8..66a8ea3e839c7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -1,13 +1,12 @@ //! Computes a normalizes-to (projection) goal for opaque types. This goal //! behaves differently depending on the current `TypingMode`. -use rustc_index::bit_set::GrowableBitSet; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::GoalSource; -use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions}; +use rustc_type_ir::{self as ty, GenericArgKind, Interner, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, inspect}; +use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; impl EvalCtxt<'_, D> where @@ -49,54 +48,29 @@ where return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); }; - // FIXME: This may have issues when the args contain aliases... - match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) { - Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => { - return self.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ); - } - Err(_) => { - return Err(NoSolution); - } - Ok(()) => {} - } - // Prefer opaques registered already. - let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args }; - // FIXME: This also unifies the previous hidden type with the expected. - // - // If that fails, we insert `expected` as a new hidden type instead of - // eagerly emitting an error. - let existing = self.probe_existing_opaque_ty(opaque_type_key); - if let Some((candidate_key, candidate_ty)) = existing { - return self - .probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup { - result: *result, - }) - .enter(|ecx| { - for (a, b) in std::iter::zip( - candidate_key.args.iter(), - opaque_type_key.args.iter(), - ) { - ecx.eq(goal.param_env, a, b)?; - } - ecx.eq(goal.param_env, candidate_ty, expected)?; - ecx.add_item_bounds_for_hidden_type( - def_id.into(), - candidate_key.args, - goal.param_env, - candidate_ty, - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); + // We structurally normalize the args so that we're able to detect defining uses + // later on. It also reduces the amount of duplicate definitions in the + // `opaque_type_storage`. + let normalized_args = + cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg| match arg.kind() { + GenericArgKind::Lifetime(lt) => Ok(lt.into()), + GenericArgKind::Type(ty) => { + self.structurally_normalize_ty(goal.param_env, ty).map(Into::into) + } + GenericArgKind::Const(ct) => { + self.structurally_normalize_const(goal.param_env, ct).map(Into::into) + } + }))?; + + let opaque_type_key = ty::OpaqueTypeKey { def_id, args: normalized_args }; + if let Some(prev) = self.register_hidden_type_in_storage(opaque_type_key, expected) + { + self.eq(goal.param_env, expected, prev)?; } - // Otherwise, define a new opaque type - let prev = self.register_hidden_type_in_storage(opaque_type_key, expected); - assert_eq!(prev, None); self.add_item_bounds_for_hidden_type( def_id.into(), - opaque_ty.args, + normalized_args, goal.param_env, expected, ); @@ -168,44 +142,3 @@ where } } } - -/// Checks whether each generic argument is simply a unique generic placeholder. -/// -/// FIXME: Interner argument is needed to constrain the `I` parameter. -fn uses_unique_placeholders_ignoring_regions( - _cx: I, - args: I::GenericArgs, -) -> Result<(), NotUniqueParam> { - let mut seen = GrowableBitSet::default(); - for arg in args.iter() { - match arg.kind() { - // Ignore regions, since we can't resolve those in a canonicalized - // query in the trait solver. - ty::GenericArgKind::Lifetime(_) => {} - ty::GenericArgKind::Type(t) => match t.kind() { - ty::Placeholder(p) => { - if !seen.insert(p.var()) { - return Err(NotUniqueParam::DuplicateParam(t.into())); - } - } - _ => return Err(NotUniqueParam::NotParam(t.into())), - }, - ty::GenericArgKind::Const(c) => match c.kind() { - ty::ConstKind::Placeholder(p) => { - if !seen.insert(p.var()) { - return Err(NotUniqueParam::DuplicateParam(c.into())); - } - } - _ => return Err(NotUniqueParam::NotParam(c.into())), - }, - } - } - - Ok(()) -} - -// FIXME: This should check for dupes and non-params first, then infer vars. -enum NotUniqueParam { - DuplicateParam(I::GenericArg), - NotParam(I::GenericArg), -} diff --git a/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs b/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs index b79926eebda3b..8ca646a9a2ddf 100644 --- a/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs +++ b/tests/ui/traits/next-solver/opaques/revealing-use-in-nested-body.rs @@ -3,6 +3,9 @@ // of the async block. This caused borrowck of the recursive // call to ICE. +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver //@ edition: 2021 //@ check-pass async fn test() { diff --git a/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs b/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs new file mode 100644 index 0000000000000..5e7e9738616cf --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// The recursive call to `foo` results in the opaque type use `opaque = ?unconstrained`. +// This needs to be supported and treated as a revealing use. + +fn foo(b: bool) -> impl Sized { + if b { + foo::(b); + } + 1u16 +} + +fn main() {} From f5e92f4a5323f52cd1367e97e87c5d159488a683 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 14 Feb 2025 00:26:30 +0000 Subject: [PATCH 04/17] Instantiate predicate binder without recanonicalizing goal in new solver --- .../src/fn_ctxt/inspect_obligations.rs | 17 +-- .../src/solve/eval_ctxt/mod.rs | 105 ++++++++---------- .../src/solve/fulfill/derive_errors.rs | 5 - .../src/traits/coherence.rs | 7 +- compiler/rustc_type_ir/src/solve/mod.rs | 2 - ...overlap-unnormalizable-projection-1.stderr | 2 +- ...didate-from-env-universe-err-1.next.stderr | 23 ---- .../candidate-from-env-universe-err-1.rs | 3 +- ...didate-from-env-universe-err-2.next.stderr | 15 --- .../candidate-from-env-universe-err-2.rs | 3 +- ...om-env-universe-err-project.current.stderr | 6 +- ...-from-env-universe-err-project.next.stderr | 35 +++--- ...candidate-from-env-universe-err-project.rs | 5 +- .../leak-check-in-selection-2.next.stderr | 23 ---- .../leak-check/leak-check-in-selection-2.rs | 3 +- .../leak-check-in-selection-3.next.stderr | 27 +---- .../leak-check-in-selection-3.old.stderr | 6 +- .../leak-check/leak-check-in-selection-3.rs | 1 - ...eck-in-selection-6-ambig-unify.next.stderr | 22 ---- ...heck-in-selection-6-ambig-unify.old.stderr | 6 +- .../leak-check-in-selection-6-ambig-unify.rs | 3 +- 21 files changed, 86 insertions(+), 233 deletions(-) delete mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.next.stderr delete mode 100644 tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr delete mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr delete mode 100644 tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index e068e60790277..468ec8a9de4bf 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -1,7 +1,6 @@ //! A utility module to inspect currently ambiguous obligations in the current context. use rustc_infer::traits::{self, ObligationCause, PredicateObligations}; -use rustc_middle::traits::solve::GoalSource; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_span::Span; use rustc_trait_selection::solve::inspect::{ @@ -119,21 +118,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> { fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); - if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) - // We do not push the instantiated forms of goals as it would cause any - // aliases referencing bound vars to go from having escaping bound vars to - // being able to be normalized to an inference variable. - // - // This is mostly just a hack as arbitrary nested goals could still contain - // such aliases while having a different `GoalSource`. Closure signature inference - // however can't really handle *every* higher ranked `Fn` goal also being present - // in the form of `?c: Fn<(>::Assoc)`. - // - // This also just better matches the behaviour of the old solver where we do not - // encounter instantiated forms of goals, only nested goals that referred to bound - // vars from instantiated goals. - && !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked) - { + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { self.obligations_for_self_ty.push(traits::Obligation::new( tcx, self.root_cause.clone(), 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 d31a82b68cb88..adc78298ddda0 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 @@ -275,9 +275,6 @@ where // corecursive functions as explained in #136824, relating types never // introduces a constructor which could cause the recursion to be guarded. GoalSource::TypeRelating => PathKind::Inductive, - // Instantiating a higher ranked goal can never cause the recursion to be - // guarded and is therefore unproductive. - GoalSource::InstantiateHigherRanked => PathKind::Inductive, // These goal sources are likely unproductive and can be changed to // `PathKind::Inductive`. Keeping them as unknown until we're confident // about this and have an example where it is necessary. @@ -507,63 +504,53 @@ where fn compute_goal(&mut self, goal: Goal) -> QueryResult { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); - if let Some(kind) = kind.no_bound_vars() { - match kind { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { - self.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r) - } - ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { - self.compute_host_effect_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => { - self.compute_projection_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => { - self.compute_type_outlives_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => { - self.compute_region_outlives_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { - self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) - } - ty::PredicateKind::Subtype(predicate) => { - self.compute_subtype_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::Coerce(predicate) => { - self.compute_coerce_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::DynCompatible(trait_def_id) => { - self.compute_dyn_compatible_goal(trait_def_id) - } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { - self.compute_well_formed_goal(Goal { param_env, predicate: term }) - } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { - self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) - } - ty::PredicateKind::ConstEquate(_, _) => { - panic!("ConstEquate should not be emitted when `-Znext-solver` is active") - } - ty::PredicateKind::NormalizesTo(predicate) => { - self.compute_normalizes_to_goal(Goal { param_env, predicate }) - } - ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self - .compute_alias_relate_goal(Goal { - param_env, - predicate: (lhs, rhs, direction), - }), - ty::PredicateKind::Ambiguous => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } + self.enter_forall(kind, |ecx, kind| match kind { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { + ecx.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r) } - } else { - self.enter_forall(kind, |ecx, kind| { - let goal = goal.with(ecx.cx(), ty::Binder::dummy(kind)); - ecx.add_goal(GoalSource::InstantiateHigherRanked, goal); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { + ecx.compute_host_effect_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => { + ecx.compute_projection_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => { + ecx.compute_type_outlives_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => { + ecx.compute_region_outlives_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + ecx.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) + } + ty::PredicateKind::Subtype(predicate) => { + ecx.compute_subtype_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Coerce(predicate) => { + ecx.compute_coerce_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::DynCompatible(trait_def_id) => { + ecx.compute_dyn_compatible_goal(trait_def_id) + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ecx.compute_well_formed_goal(Goal { param_env, predicate: arg }) + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { + ecx.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) + } + ty::PredicateKind::ConstEquate(_, _) => { + panic!("ConstEquate should not be emitted when `-Znext-solver` is active") + } + ty::PredicateKind::NormalizesTo(predicate) => { + ecx.compute_normalizes_to_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::AliasRelate(lhs, rhs, direction) => { + ecx.compute_alias_relate_goal(Goal { param_env, predicate: (lhs, rhs, direction) }) + } + ty::PredicateKind::Ambiguous => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + }) } // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index f64cd5ffebe3d..1464bffd95391 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -227,7 +227,6 @@ impl<'tcx> BestObligation<'tcx> { nested_goal.source(), GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition - | GoalSource::InstantiateHigherRanked | GoalSource::AliasWellFormed ) && nested_goal.result().is_err() }, @@ -517,10 +516,6 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { )); impl_where_bound_count += 1; } - // Skip over a higher-ranked predicate. - (_, GoalSource::InstantiateHigherRanked) => { - obligation = self.obligation.clone(); - } (ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { obligation = make_obligation(self.obligation.cause.clone()); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 93c7dae9c5be6..f5ed4377eedcb 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -661,9 +661,10 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { // For bound predicates we simply call `infcx.enter_forall` // and then prove the resulting predicate as a nested goal. let Goal { param_env, predicate } = goal.goal(); - let trait_ref = match predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref, - Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) + let predicate_kind = goal.infcx().enter_forall_and_leak_universe(predicate.kind()); + let trait_ref = match predicate_kind { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr)) => tr.trait_ref, + ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) if matches!( infcx.tcx.def_kind(proj.projection_term.def_id), DefKind::AssocTy | DefKind::AssocConst diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 2e05c23a6458c..557aa69d77628 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -80,8 +80,6 @@ pub enum GoalSource { ImplWhereBound, /// Const conditions that need to hold for `~const` alias bounds to hold. AliasBoundConstCondition, - /// Instantiating a higher-ranked goal and re-proving it. - InstantiateHigherRanked, /// Predicate required for an alias projection to be well-formed. /// This is used in three places: /// 1. projecting to an opaque whose hidden type is already registered in diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.stderr b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.stderr index 22673cef64079..c92854b92f9d3 100644 --- a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.stderr +++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.stderr @@ -12,7 +12,7 @@ LL | impl Trait for Box {} | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>` | = note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>` - = note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<_>` + = note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box< as WithAssoc<'a>>::Assoc>` error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.next.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.next.stderr deleted file mode 100644 index 90391b7b86b5c..0000000000000 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.next.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0277]: the trait bound `for<'a> &'a &T: Trait` is not satisfied - --> $DIR/candidate-from-env-universe-err-1.rs:27:16 - | -LL | hr_bound::<&T>(); - | ^^ the trait `for<'a> Trait` is not implemented for `&'a &T` - | -note: required by a bound in `hr_bound` - --> $DIR/candidate-from-env-universe-err-1.rs:14:20 - | -LL | fn hr_bound() - | -------- required by a bound in this function -LL | where -LL | for<'a> &'a T: Trait, - | ^^^^^ required by this bound in `hr_bound` -help: consider removing the leading `&`-reference - | -LL - hr_bound::<&T>(); -LL + hr_bound::(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs index bd251216162db..1fc08b2abcead 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-1.rs @@ -1,6 +1,6 @@ //@ revisions: old next //@[next] compile-flags: -Znext-solver -//@[old] check-pass +//@ check-pass // cc #119820 @@ -25,7 +25,6 @@ where // the leak check both candidates may apply and we prefer the // `param_env` candidate in winnowing. hr_bound::<&T>(); - //[next]~^ ERROR the trait bound `for<'a> &'a &T: Trait` is not satisfied } fn main() {} diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr deleted file mode 100644 index 0f5a9de7ab854..0000000000000 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.next.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0277]: the trait bound `for<'a> T: Trait<'a, '_>` is not satisfied - --> $DIR/candidate-from-env-universe-err-2.rs:15:15 - | -LL | impl_hr::(); - | ^ the trait `for<'a> Trait<'a, '_>` is not implemented for `T` - | -note: required by a bound in `impl_hr` - --> $DIR/candidate-from-env-universe-err-2.rs:12:19 - | -LL | fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {} - | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impl_hr` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs index 0132b7db60574..dcdff28ad15e9 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-2.rs @@ -1,6 +1,6 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver -//@[current] check-pass +//@ check-pass // cc #119820 @@ -13,7 +13,6 @@ fn impl_hr<'b, T: for<'a> Trait<'a, 'b>>() {} fn not_hr<'a, T: for<'b> Trait<'a, 'b> + OtherTrait<'static>>() { impl_hr::(); - //[next]~^ ERROR the trait bound `for<'a> T: Trait<'a, '_>` is not satisfied } fn main() {} diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr index c8394575e71de..324e1571a936a 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.current.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/candidate-from-env-universe-err-project.rs:38:5 + --> $DIR/candidate-from-env-universe-err-project.rs:37:5 | LL | projection_bound::(); | ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other @@ -13,7 +13,7 @@ LL | fn projection_bound Trait<'a, Assoc = usize>>() {} | ^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/candidate-from-env-universe-err-project.rs:52:30 + --> $DIR/candidate-from-env-universe-err-project.rs:51:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other @@ -22,7 +22,7 @@ LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); found associated type `>::Assoc` error[E0308]: mismatched types - --> $DIR/candidate-from-env-universe-err-project.rs:52:30 + --> $DIR/candidate-from-env-universe-err-project.rs:51:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr index 468dc3b082e5c..310d2b3d33501 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr @@ -1,41 +1,34 @@ -error[E0277]: the trait bound `for<'a> T: Trait<'a>` is not satisfied - --> $DIR/candidate-from-env-universe-err-project.rs:28:19 - | -LL | trait_bound::(); - | ^ the trait `for<'a> Trait<'a>` is not implemented for `T` - | -note: required by a bound in `trait_bound` - --> $DIR/candidate-from-env-universe-err-project.rs:17:19 - | -LL | fn trait_bound Trait<'a>>() {} - | ^^^^^^^^^^^^^^^^^ required by this bound in `trait_bound` - -error[E0277]: the trait bound `for<'a> T: Trait<'a>` is not satisfied - --> $DIR/candidate-from-env-universe-err-project.rs:38:24 +error[E0271]: type mismatch resolving `>::Assoc == usize` + --> $DIR/candidate-from-env-universe-err-project.rs:37:24 | LL | projection_bound::(); - | ^ the trait `for<'a> Trait<'a>` is not implemented for `T` + | ^ type mismatch resolving `>::Assoc == usize` + | +note: types differ + --> $DIR/candidate-from-env-universe-err-project.rs:14:18 | +LL | type Assoc = usize; + | ^^^^^ note: required by a bound in `projection_bound` - --> $DIR/candidate-from-env-universe-err-project.rs:18:24 + --> $DIR/candidate-from-env-universe-err-project.rs:18:42 | LL | fn projection_bound Trait<'a, Assoc = usize>>() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `projection_bound` + | ^^^^^^^^^^^^^ required by this bound in `projection_bound` error: higher-ranked subtype error - --> $DIR/candidate-from-env-universe-err-project.rs:52:30 + --> $DIR/candidate-from-env-universe-err-project.rs:51:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: higher-ranked subtype error - --> $DIR/candidate-from-env-universe-err-project.rs:52:30 + --> $DIR/candidate-from-env-universe-err-project.rs:51:30 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs index d70e392238218..1cf3052f3af8c 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs @@ -20,13 +20,12 @@ fn projection_bound Trait<'a, Assoc = usize>>() {} // We use a function with a trivial where-bound which is more // restrictive than the impl. fn function1>() { - // err + // ok // // Proving `for<'a> T: Trait<'a>` using the where-bound does not // result in a leak check failure even though it does not apply. // We prefer env candidates over impl candidatescausing this to succeed. trait_bound::(); - //[next]~^ ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied } fn function2>() { @@ -36,7 +35,7 @@ fn function2>() { // does not use the leak check when trying the where-bound, causing us // to prefer it over the impl, resulting in a placeholder error. projection_bound::(); - //[next]~^ ERROR the trait bound `for<'a> T: Trait<'a>` is not satisfied + //[next]~^ ERROR type mismatch resolving `>::Assoc == usize` //[current]~^^ ERROR mismatched types } diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr deleted file mode 100644 index cb97bc4b8fc6e..0000000000000 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.next.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0283]: type annotations needed - --> $DIR/leak-check-in-selection-2.rs:17:5 - | -LL | impls_trait::<(), _>(); - | ^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` - | -note: multiple `impl`s satisfying `for<'a> (): Trait<&'a str, _>` found - --> $DIR/leak-check-in-selection-2.rs:10:1 - | -LL | impl<'a> Trait<&'a str, &'a str> for () {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | impl<'a> Trait<&'a str, String> for () {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `impls_trait` - --> $DIR/leak-check-in-selection-2.rs:14:19 - | -LL | fn impls_trait Trait<&'a str, U>, U>() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls_trait` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs index 24e38ec45a2c4..67188b17957ac 100644 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-2.rs @@ -1,6 +1,6 @@ //@ revisions: old next //@[next] compile-flags: -Znext-solver -//@[old] check-pass +//@ check-pass // cc #119820 @@ -15,5 +15,4 @@ fn impls_trait Trait<&'a str, U>, U>() {} fn main() { impls_trait::<(), _>(); - //[next]~^ ERROR type annotations needed } diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr index 5be683cd3191b..990586601748a 100644 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.next.stderr @@ -1,24 +1,5 @@ error[E0283]: type annotations needed - --> $DIR/leak-check-in-selection-3.rs:18:5 - | -LL | impls_leak::>(); - | ^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_leak` - | -note: multiple `impl`s satisfying `for<'a> Box<_>: Leak<'a>` found - --> $DIR/leak-check-in-selection-3.rs:9:1 - | -LL | impl Leak<'_> for Box {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | impl Leak<'static> for Box {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `impls_leak` - --> $DIR/leak-check-in-selection-3.rs:12:18 - | -LL | fn impls_leak Leak<'a>>() {} - | ^^^^^^^^^^^^^^^^ required by this bound in `impls_leak` - -error[E0283]: type annotations needed - --> $DIR/leak-check-in-selection-3.rs:35:5 + --> $DIR/leak-check-in-selection-3.rs:34:5 | LL | impls_indirect_leak::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_indirect_leak` @@ -31,18 +12,18 @@ LL | impl Leak<'_> for Box {} LL | impl Leak<'static> for Box {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: required for `Box<_>` to implement `for<'a> IndirectLeak<'a>` - --> $DIR/leak-check-in-selection-3.rs:23:23 + --> $DIR/leak-check-in-selection-3.rs:22:23 | LL | impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} | -------- ^^^^^^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here note: required by a bound in `impls_indirect_leak` - --> $DIR/leak-check-in-selection-3.rs:25:27 + --> $DIR/leak-check-in-selection-3.rs:24:27 | LL | fn impls_indirect_leak IndirectLeak<'a>>() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls_indirect_leak` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr index 194571dd4a85b..d78a90209f33a 100644 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.old.stderr @@ -1,5 +1,5 @@ error[E0283]: type annotations needed - --> $DIR/leak-check-in-selection-3.rs:35:5 + --> $DIR/leak-check-in-selection-3.rs:34:5 | LL | impls_indirect_leak::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_indirect_leak` @@ -12,14 +12,14 @@ LL | impl Leak<'_> for Box {} LL | impl Leak<'static> for Box {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: required for `Box<_>` to implement `for<'a> IndirectLeak<'a>` - --> $DIR/leak-check-in-selection-3.rs:23:23 + --> $DIR/leak-check-in-selection-3.rs:22:23 | LL | impl<'a, T: Leak<'a>> IndirectLeak<'a> for T {} | -------- ^^^^^^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here note: required by a bound in `impls_indirect_leak` - --> $DIR/leak-check-in-selection-3.rs:25:27 + --> $DIR/leak-check-in-selection-3.rs:24:27 | LL | fn impls_indirect_leak IndirectLeak<'a>>() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls_indirect_leak` diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs index 9aa1be57a4fb2..c51dd797e24f9 100644 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-3.rs @@ -16,7 +16,6 @@ fn direct() { // The `Box` impls fails the leak check, // meaning that we apply the `Box` impl. impls_leak::>(); - //[next]~^ ERROR type annotations needed } trait IndirectLeak<'a> {} diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr deleted file mode 100644 index a12e3312230af..0000000000000 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0283]: type annotations needed - --> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5 - | -LL | impls_trait::<(), _, _>() - | ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` - | -note: multiple `impl`s satisfying `(): Trait<_, _>` found - --> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1 - | -LL | impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | impl Trait for () {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `impls_trait` - --> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19 - | -LL | fn impls_trait, U: Id, V>() {} - | ^^^^^^^^^^^ required by this bound in `impls_trait` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr index a12e3312230af..8a69add08afcf 100644 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr @@ -1,18 +1,18 @@ error[E0283]: type annotations needed - --> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5 + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:31:5 | LL | impls_trait::<(), _, _>() | ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` | note: multiple `impl`s satisfying `(): Trait<_, _>` found - --> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1 + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:27:1 | LL | impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | impl Trait for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `impls_trait` - --> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19 + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:29:19 | LL | fn impls_trait, U: Id, V>() {} | ^^^^^^^^^^^ required by this bound in `impls_trait` diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs index 592d581695e52..47063b118b82d 100644 --- a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs @@ -1,5 +1,6 @@ //@ revisions: old next //@[next] compile-flags: -Znext-solver +//@[next] check-pass // The new trait solver does not return region constraints if the goal // is still ambiguous. This should cause the following test to fail with @@ -28,5 +29,5 @@ impl Trait for () {} fn impls_trait, U: Id, V>() {} fn main() { impls_trait::<(), _, _>() - //~^ ERROR type annotations needed + //[old]~^ ERROR type annotations needed } From 6499f506ef0d64dfe40be0c9e51df666db86080b Mon Sep 17 00:00:00 2001 From: lcnr Date: Sun, 27 Apr 2025 22:28:03 +0000 Subject: [PATCH 05/17] eagerly compute `sub_relations` again While still only using them for diagnostics. We could use them for cycle detection in generalization and it seems desirable to do so in the future. However, this is unsound with the old trait solver as its cache does not track these `sub_relations` in any way. We would also need to consider them when canonicalizing as otherwise instantiating the canonical response may fail. --- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 7 -- compiler/rustc_infer/src/infer/context.rs | 4 + compiler/rustc_infer/src/infer/mod.rs | 5 ++ .../src/infer/relate/generalize.rs | 4 + .../src/infer/snapshot/undo_log.rs | 4 +- .../rustc_infer/src/infer/type_variable.rs | 79 +++++++++++++++++- .../src/solve/eval_ctxt/mod.rs | 4 + .../rustc_next_trait_solver/src/solve/mod.rs | 14 ++-- .../src/error_reporting/infer/mod.rs | 1 - .../error_reporting/infer/need_type_info.rs | 2 +- .../error_reporting/infer/sub_relations.rs | 81 ------------------- .../src/error_reporting/mod.rs | 4 - .../src/error_reporting/traits/mod.rs | 4 - compiler/rustc_type_ir/src/infer_ctxt.rs | 1 + 14 files changed, 107 insertions(+), 107 deletions(-) delete mode 100644 compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index de189b301092c..54089b95bb4fb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -19,7 +19,6 @@ use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym}; use rustc_trait_selection::error_reporting::TypeErrCtxt; -use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use crate::coercion::DynamicCoerceMany; @@ -177,14 +176,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// [`InferCtxtErrorExt::err_ctxt`]: rustc_trait_selection::error_reporting::InferCtxtErrorExt::err_ctxt pub(crate) fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { - let mut sub_relations = SubRelations::default(); - sub_relations.add_constraints( - self, - self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate), - ); TypeErrCtxt { infcx: &self.infcx, - sub_relations: RefCell::new(sub_relations), typeck_results: Some(self.typeck_results.borrow()), fallback_has_occurred: self.fallback_has_occurred.get(), normalize_fn_sig: Box::new(|fn_sig| { diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 359b9da11ced6..847715c6b3d3f 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -128,6 +128,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.inner.borrow_mut().type_variables().equate(a, b); } + fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.inner.borrow_mut().type_variables().sub(a, b); + } + fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid) { self.inner.borrow_mut().int_unification_table().union(a, b); } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 810cc5e8db446..07e290349d60a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -724,6 +724,7 @@ impl<'tcx> InferCtxt<'tcx> { let r_b = self.shallow_resolve(predicate.skip_binder().b); match (r_a.kind(), r_b.kind()) { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { + self.inner.borrow_mut().type_variables().sub(a_vid, b_vid); return Err((a_vid, b_vid)); } _ => {} @@ -1060,6 +1061,10 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().type_variables().root_var(var) } + pub fn sub_root_var(&self, var: ty::TyVid) -> ty::TyVid { + self.inner.borrow_mut().type_variables().sub_root_var(var) + } + pub fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { self.inner.borrow_mut().const_unification_table().find(var).vid } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 210b8f37d883d..cdcd6f6dbb6ff 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -503,6 +503,10 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { let origin = inner.type_variables().var_origin(vid); let new_var_id = inner.type_variables().new_var(self.for_universe, origin); + // Record that `vid` and `new_var_id` have to be subtypes + // of each other. This is currently only used for diagnostics. + // To see why, see the docs in the `type_variables` module. + inner.type_variables().sub(vid, new_var_id); // If we're in the new solver and create a new inference // variable inside of an alias we eagerly constrain that // inference variable to prevent unexpected ambiguity errors. diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index b7412d3d6a6da..6b2f9df6bc80c 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -19,7 +19,7 @@ pub struct Snapshot<'tcx> { pub(crate) enum UndoLog<'tcx> { DuplicateOpaqueType, OpaqueTypes(OpaqueTypeKey<'tcx>, Option>), - TypeVariables(sv::UndoLog>>), + TypeVariables(type_variable::UndoLog<'tcx>), ConstUnificationTable(sv::UndoLog>>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), @@ -45,7 +45,9 @@ macro_rules! impl_from { impl_from! { RegionConstraintCollector(region_constraints::UndoLog<'tcx>), + TypeVariables(type_variable::UndoLog<'tcx>), TypeVariables(sv::UndoLog>>), + TypeVariables(sv::UndoLog>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 2086483b94a71..cb64c17bdff33 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -13,9 +13,33 @@ use tracing::debug; use crate::infer::InferCtxtUndoLogs; -impl<'tcx> Rollback>>> for TypeVariableStorage<'tcx> { - fn reverse(&mut self, undo: sv::UndoLog>>) { - self.eq_relations.reverse(undo) +/// Represents a single undo-able action that affects a type inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'tcx> { + EqRelation(sv::UndoLog>>), + SubRelation(sv::UndoLog>), +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>>) -> Self { + UndoLog::EqRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>) -> Self { + UndoLog::SubRelation(l) + } +} + +impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo), + } } } @@ -27,6 +51,24 @@ pub(crate) struct TypeVariableStorage<'tcx> { /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. eq_relations: ut::UnificationTableStorage>, + + /// Only used by `-Znext-solver` andfor diagnostics. + /// + /// When reporting ambiguity errors, we sometimes want to + /// treat all inference vars which are subtypes of each + /// others as if they are equal. For this case we compute + /// the transitive closure of our subtype obligations here. + /// + /// E.g. when encountering ambiguity errors, we want to suggest + /// specifying some method argument or to add a type annotation + /// to a local variable. Because subtyping cannot change the + /// shape of a type, it's fine if the cause of the ambiguity error + /// is only related to the suggested variable via subtyping. + /// + /// Even for something like `let x = returns_arg(); x.method();` the + /// type of `x` is only a supertype of the argument of `returns_arg`. We + /// still want to suggest specifying the type of the argument. + sub_relations: ut::UnificationTableStorage, } pub(crate) struct TypeVariableTable<'a, 'tcx> { @@ -109,6 +151,16 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); self.eq_relations().union(a, b); + self.sub_relations().union(a, b); + } + + /// Records that `a <: b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub(crate) fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.sub_relations().union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -142,6 +194,10 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { origin: TypeVariableOrigin, ) -> ty::TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + + let sub_key = self.sub_relations().new_key(()); + debug_assert_eq!(eq_key.vid, sub_key); + let index = self.storage.values.push(TypeVariableData { origin }); debug_assert_eq!(eq_key.vid, index); @@ -164,6 +220,18 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { self.eq_relations().find(vid).vid } + /// Returns the "root" variable of `vid` in the `sub_relations` + /// equivalence table. All type variables that have been are + /// related via equality or subtyping will yield the same root + /// variable (per the union-find algorithm), so `sub_root_var(a) + /// == sub_root_var(b)` implies that: + /// ```text + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ``` + pub(crate) fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + self.sub_relations().find(vid) + } + /// Retrieves the type to which `vid` has been instantiated, if /// any. pub(crate) fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { @@ -181,6 +249,11 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { self.storage.eq_relations.with_log(self.undo_log) } + #[inline] + fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> { + self.storage.sub_relations.with_log(self.undo_log) + } + /// Returns a range of the type variables created during the snapshot. pub(crate) fn vars_since_snapshot( &mut self, 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 adc78298ddda0..40388a480d0dc 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 @@ -838,6 +838,10 @@ where && goal.param_env.visit_with(&mut visitor).is_continue() } + pub(super) fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.delegate.sub_ty_vids_raw(a, b) + } + #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn eq>( &mut self, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 8173146e2fe24..2e2cdddf47406 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -120,11 +120,15 @@ where #[instrument(level = "trace", skip(self))] fn compute_subtype_goal(&mut self, goal: Goal>) -> QueryResult { - if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } else { - self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + match (goal.predicate.a.kind(), goal.predicate.b.kind()) { + (ty::Infer(ty::TyVar(a_vid)), ty::Infer(ty::TyVar(b_vid))) => { + self.sub_ty_vids_raw(a_vid, b_vid); + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + _ => { + self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index fdd547448f004..00230f45a892e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -92,7 +92,6 @@ mod suggest; pub mod need_type_info; pub mod nice_region_error; pub mod region; -pub mod sub_relations; /// Makes a valid string literal from a string by escaping special characters (" and \), /// unless they are already escaped. diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index cb1c9c7536903..4544b667481ad 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -903,7 +903,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { - self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid) + self.tecx.sub_root_var(a_vid) == self.tecx.sub_root_var(b_vid) } _ => false, } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs deleted file mode 100644 index ef26a8ff7b863..0000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs +++ /dev/null @@ -1,81 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::undo_log::NoUndo; -use rustc_data_structures::unify as ut; -use rustc_middle::ty; - -use crate::infer::InferCtxt; - -#[derive(Debug, Copy, Clone, PartialEq)] -struct SubId(u32); -impl ut::UnifyKey for SubId { - type Value = (); - #[inline] - fn index(&self) -> u32 { - self.0 - } - #[inline] - fn from_index(i: u32) -> SubId { - SubId(i) - } - fn tag() -> &'static str { - "SubId" - } -} - -/// When reporting ambiguity errors, we sometimes want to -/// treat all inference vars which are subtypes of each -/// others as if they are equal. For this case we compute -/// the transitive closure of our subtype obligations here. -/// -/// E.g. when encountering ambiguity errors, we want to suggest -/// specifying some method argument or to add a type annotation -/// to a local variable. Because subtyping cannot change the -/// shape of a type, it's fine if the cause of the ambiguity error -/// is only related to the suggested variable via subtyping. -/// -/// Even for something like `let x = returns_arg(); x.method();` the -/// type of `x` is only a supertype of the argument of `returns_arg`. We -/// still want to suggest specifying the type of the argument. -#[derive(Default)] -pub struct SubRelations { - map: FxHashMap, - table: ut::UnificationTableStorage, -} - -impl SubRelations { - fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId { - let root_vid = infcx.root_var(vid); - *self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(())) - } - - pub fn add_constraints<'tcx>( - &mut self, - infcx: &InferCtxt<'tcx>, - obls: impl IntoIterator>, - ) { - for p in obls { - let (a, b) = match p.kind().skip_binder() { - ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { - (a, b) - } - ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b), - _ => continue, - }; - - match (a.kind(), b.kind()) { - (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { - let a = self.get_id(infcx, a_vid); - let b = self.get_id(infcx, b_vid); - self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap(); - } - _ => continue, - } - } - } - - pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool { - let a = self.get_id(infcx, a); - let b = self.get_id(infcx, b); - self.table.with_log(&mut NoUndo).unioned(a, b) - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs index 82695688ae897..cce20b05c79aa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs @@ -7,8 +7,6 @@ use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::ty::{self, Ty}; -use crate::error_reporting::infer::sub_relations; - pub mod infer; pub mod traits; @@ -21,7 +19,6 @@ pub mod traits; /// methods which should not be used during the happy path. pub struct TypeErrCtxt<'a, 'tcx> { pub infcx: &'a InferCtxt<'tcx>, - pub sub_relations: std::cell::RefCell, pub typeck_results: Option>>, pub fallback_has_occurred: bool, @@ -38,7 +35,6 @@ impl<'tcx> InferCtxt<'tcx> { fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { TypeErrCtxt { infcx: self, - sub_relations: Default::default(), typeck_results: None, fallback_has_occurred: false, normalize_fn_sig: Box::new(|fn_sig| fn_sig), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 78f9287b407b3..9d2d64e4398fc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -141,10 +141,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, mut errors: Vec>, ) -> ErrorGuaranteed { - self.sub_relations - .borrow_mut() - .add_constraints(self, errors.iter().map(|e| e.obligation.predicate)); - #[derive(Debug)] struct ErrorDescriptor<'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index c149076211739..06a054ebdb491 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -188,6 +188,7 @@ pub trait InferCtxtLike: Sized { ) -> U; fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid); + fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid); fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid); fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid); fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid); From fd92e4e77c25c3b4cf73c079bda2402fd7bd9a7c Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 28 Apr 2025 17:21:36 +0000 Subject: [PATCH 06/17] inline `CanonicalTyVarKind` --- .../src/infer/canonical/canonicalizer.rs | 23 ++------ .../rustc_infer/src/infer/canonical/mod.rs | 15 +---- compiler/rustc_middle/src/infer/canonical.rs | 2 +- .../src/canonicalizer.rs | 12 ++-- compiler/rustc_type_ir/src/canonical.rs | 59 ++++++++----------- 5 files changed, 40 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index a1a0926cd8188..925098ee4072c 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -17,8 +17,7 @@ use tracing::debug; use crate::infer::InferCtxt; use crate::infer::canonical::{ - Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, - OriginalQueryValues, + Canonical, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarKind, OriginalQueryValues, }; impl<'tcx> InferCtxt<'tcx> { @@ -368,9 +367,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { ui = ty::UniverseIndex::ROOT; } self.canonicalize_ty_var( - CanonicalVarInfo { - kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - }, + CanonicalVarInfo { kind: CanonicalVarKind::Ty(ui) }, t, ) } @@ -382,10 +379,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { if nt != t { return self.fold_ty(nt); } else { - self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) }, - t, - ) + self.canonicalize_ty_var(CanonicalVarInfo { kind: CanonicalVarKind::Int }, t) } } ty::Infer(ty::FloatVar(vid)) => { @@ -393,10 +387,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { if nt != t { return self.fold_ty(nt); } else { - self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) }, - t, - ) + self.canonicalize_ty_var(CanonicalVarInfo { kind: CanonicalVarKind::Float }, t) } } @@ -690,12 +681,10 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { .iter() .map(|v| CanonicalVarInfo { kind: match v.kind { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + CanonicalVarKind::Int | CanonicalVarKind::Float => { return *v; } - CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) - } + CanonicalVarKind::Ty(u) => CanonicalVarKind::Ty(reverse_universe_map[&u]), CanonicalVarKind::Region(u) => { CanonicalVarKind::Region(reverse_universe_map[&u]) } diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 3be07dbe208f9..08df0f41bd982 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -108,18 +108,9 @@ impl<'tcx> InferCtxt<'tcx> { universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> GenericArg<'tcx> { match cv_info.kind { - CanonicalVarKind::Ty(ty_kind) => { - let ty = match ty_kind { - CanonicalTyVarKind::General(ui) => { - self.next_ty_var_in_universe(span, universe_map(ui)) - } - - CanonicalTyVarKind::Int => self.next_int_var(), - - CanonicalTyVarKind::Float => self.next_float_var(), - }; - ty.into() - } + CanonicalVarKind::Ty(ui) => self.next_ty_var_in_universe(span, universe_map(ui)).into(), + CanonicalVarKind::Int => self.next_int_var().into(), + CanonicalVarKind::Float => self.next_float_var().into(), CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, bound }) => { let universe_mapped = universe_map(universe); diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 5b8603744961c..15cc01ba6a01e 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -27,7 +27,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; pub use rustc_type_ir as ir; -pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind}; +pub use rustc_type_ir::CanonicalVarKind; use smallvec::SmallVec; use crate::mir::ConstraintCategory; diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index bbb4a162027dc..bac5767c457ff 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -4,8 +4,8 @@ use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack}; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; use rustc_type_ir::{ - self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike, - Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self as ty, Canonical, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike, Interner, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use crate::delegate::SolverDelegate; @@ -323,11 +323,11 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { "ty vid should have been resolved fully before canonicalization" ); - CanonicalVarKind::Ty(CanonicalTyVarKind::General( + CanonicalVarKind::Ty( self.delegate .universe_of_ty(vid) .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")), - )) + ) } ty::IntVar(vid) => { assert_eq!( @@ -335,7 +335,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { t, "ty vid should have been resolved fully before canonicalization" ); - CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + CanonicalVarKind::Int } ty::FloatVar(vid) => { assert_eq!( @@ -343,7 +343,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { t, "ty vid should have been resolved fully before canonicalization" ); - CanonicalVarKind::Ty(CanonicalTyVarKind::Float) + CanonicalVarKind::Float } ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { panic!("fresh vars not expected in canonicalization") diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 67b67df4b2817..9bf16e1a94143 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -110,7 +110,7 @@ impl CanonicalVarInfo { pub fn is_existential(&self) -> bool { match self.kind { - CanonicalVarKind::Ty(_) => true, + CanonicalVarKind::Ty(_) | CanonicalVarKind::Int | CanonicalVarKind::Float => true, CanonicalVarKind::PlaceholderTy(_) => false, CanonicalVarKind::Region(_) => true, CanonicalVarKind::PlaceholderRegion(..) => false, @@ -123,6 +123,8 @@ impl CanonicalVarInfo { match self.kind { CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, CanonicalVarKind::Ty(_) + | CanonicalVarKind::Int + | CanonicalVarKind::Float | CanonicalVarKind::PlaceholderTy(_) | CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => false, @@ -131,7 +133,11 @@ impl CanonicalVarInfo { pub fn expect_placeholder_index(self) -> usize { match self.kind { - CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => { + CanonicalVarKind::Ty(_) + | CanonicalVarKind::Int + | CanonicalVarKind::Float + | CanonicalVarKind::Region(_) + | CanonicalVarKind::Const(_) => { panic!("expected placeholder: {self:?}") } @@ -151,8 +157,14 @@ impl CanonicalVarInfo { derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) )] pub enum CanonicalVarKind { - /// Some kind of type inference variable. - Ty(CanonicalTyVarKind), + /// A general type variable `?T` that can be unified with arbitrary types. + Ty(UniverseIndex), + + /// Integral type variable `?I` (that can only be unified with integral types). + Int, + + /// Floating-point type variable `?F` (that can only be unified with float types). + Float, /// A "placeholder" that represents "any type". PlaceholderTy(I::PlaceholderTy), @@ -175,15 +187,13 @@ pub enum CanonicalVarKind { impl CanonicalVarKind { pub fn universe(self) -> UniverseIndex { match self { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => ui, + CanonicalVarKind::Ty(ui) => ui, CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::Const(ui) => ui, CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe(), CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe(), CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe(), - CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => { - UniverseIndex::ROOT - } + CanonicalVarKind::Int | CanonicalVarKind::Float => UniverseIndex::ROOT, } } @@ -193,9 +203,7 @@ impl CanonicalVarKind { /// the updated universe is not the root. pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarKind { match self { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) - } + CanonicalVarKind::Ty(_) => CanonicalVarKind::Ty(ui), CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui), CanonicalVarKind::Const(_) => CanonicalVarKind::Const(ui), @@ -208,7 +216,7 @@ impl CanonicalVarKind { CanonicalVarKind::PlaceholderConst(placeholder) => { CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui)) } - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + CanonicalVarKind::Int | CanonicalVarKind::Float => { assert_eq!(ui, UniverseIndex::ROOT); self } @@ -216,28 +224,6 @@ impl CanonicalVarKind { } } -/// Rust actually has more than one category of type variables; -/// notably, the type variables we create for literals (e.g., 22 or -/// 22.) can only be instantiated with integral/float types (e.g., -/// usize or f32). In order to faithfully reproduce a type, we need to -/// know what set of types a given type variable can be unified with. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -pub enum CanonicalTyVarKind { - /// General type variable `?T` that can be unified with arbitrary types. - General(UniverseIndex), - - /// Integral type variable `?I` (that can only be unified with integral types). - Int, - - /// Floating-point type variable `?F` (that can only be unified with float types). - Float, -} - /// A set of values corresponding to the canonical variables from some /// `Canonical`. You can give these values to /// `canonical_value.instantiate` to instantiate them into the canonical @@ -311,7 +297,10 @@ impl CanonicalVarValues { var_values: cx.mk_args_from_iter(infos.iter().enumerate().map( |(i, info)| -> I::GenericArg { match info.kind { - CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => { + CanonicalVarKind::Ty(_) + | CanonicalVarKind::Int + | CanonicalVarKind::Float + | CanonicalVarKind::PlaceholderTy(_) => { Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) .into() } From 18f6382f1925fedbade56e88a6809007306c1306 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 28 Apr 2025 18:29:48 +0000 Subject: [PATCH 07/17] track relevant sub_relations in canonical queries --- .../src/infer/canonical/canonicalizer.rs | 18 ++++- .../rustc_infer/src/infer/canonical/mod.rs | 23 ++++-- .../src/infer/canonical/query_response.rs | 68 ++++++++++------- compiler/rustc_infer/src/infer/context.rs | 3 + .../src/canonicalizer.rs | 22 ++++-- .../rustc_next_trait_solver/src/delegate.rs | 1 + .../src/solve/eval_ctxt/canonical.rs | 73 +++++++++++-------- .../src/solve/delegate.rs | 3 +- compiler/rustc_type_ir/src/canonical.rs | 20 +++-- compiler/rustc_type_ir/src/infer_ctxt.rs | 1 + 10 files changed, 156 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 925098ee4072c..27b218220f1cc 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -298,6 +298,7 @@ struct Canonicalizer<'cx, 'tcx> { // Note that indices is only used once `var_values` is big enough to be // heap-allocated. indices: FxHashMap, BoundVar>, + sub_root_lookup_table: FxHashMap, canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, @@ -366,8 +367,11 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { // FIXME: perf problem described in #55921. ui = ty::UniverseIndex::ROOT; } + let sub_root = self.get_or_insert_sub_root(vid); self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(ui) }, + CanonicalVarInfo { + kind: CanonicalVarKind::Ty { universe: ui, sub_root }, + }, t, ) } @@ -567,6 +571,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { variables: SmallVec::from_slice(base.variables), query_state, indices: FxHashMap::default(), + sub_root_lookup_table: Default::default(), binder_index: ty::INNERMOST, }; if canonicalizer.query_state.var_values.spilled() { @@ -661,6 +666,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } } + fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar { + let root_vid = self.infcx.unwrap().sub_root_var(vid); + let idx = + *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); + ty::BoundVar::from(idx) + } + /// Replaces the universe indexes used in `var_values` with their index in /// `query_state.universe_map`. This minimizes the maximum universe used in /// the canonicalized value. @@ -684,7 +696,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { CanonicalVarKind::Int | CanonicalVarKind::Float => { return *v; } - CanonicalVarKind::Ty(u) => CanonicalVarKind::Ty(reverse_universe_map[&u]), + CanonicalVarKind::Ty { universe, sub_root } => { + CanonicalVarKind::Ty { universe: reverse_universe_map[&universe], sub_root } + } CanonicalVarKind::Region(u) => { CanonicalVarKind::Region(reverse_universe_map[&u]) } diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 08df0f41bd982..e8d9138e5deaa 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -84,13 +84,12 @@ impl<'tcx> InferCtxt<'tcx> { variables: &List>, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> CanonicalVarValues<'tcx> { - CanonicalVarValues { - var_values: self.tcx.mk_args_from_iter( - variables - .iter() - .map(|info| self.instantiate_canonical_var(span, info, &universe_map)), - ), + let mut var_values = Vec::new(); + for info in variables.iter() { + let value = self.instantiate_canonical_var(span, info, &var_values, &universe_map); + var_values.push(value); } + CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) } } /// Given the "info" about a canonical variable, creates a fresh @@ -105,10 +104,20 @@ impl<'tcx> InferCtxt<'tcx> { &self, span: Span, cv_info: CanonicalVarInfo<'tcx>, + previous_var_values: &[GenericArg<'tcx>], universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> GenericArg<'tcx> { match cv_info.kind { - CanonicalVarKind::Ty(ui) => self.next_ty_var_in_universe(span, universe_map(ui)).into(), + CanonicalVarKind::Ty { universe, sub_root } => { + let vid = self.next_ty_var_id_in_universe(span, universe_map(universe)); + if let Some(prev) = previous_var_values.get(sub_root.as_usize()) { + let &ty::Infer(ty::TyVar(sub_root)) = prev.expect_ty().kind() else { + unreachable!("expected `sub_root` to be an inference variable"); + }; + self.inner.borrow_mut().type_variables().sub(vid, sub_root); + } + Ty::new_var(self.tcx, vid).into() + } CanonicalVarKind::Int => self.next_int_var().into(), CanonicalVarKind::Float => self.next_float_var().into(), diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 1ae864c454f28..227d2817e496b 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -13,7 +13,9 @@ use std::iter; use rustc_index::{Idx, IndexVec}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{ + self, BoundVar, CanonicalVarKind, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable, +}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument}; @@ -443,32 +445,48 @@ impl<'tcx> InferCtxt<'tcx> { // Create result arguments: if we found a value for a // given variable in the loop above, use that. Otherwise, use // a fresh inference variable. - let result_args = CanonicalVarValues { - var_values: self.tcx.mk_args_from_iter( - query_response.variables.iter().enumerate().map(|(index, info)| { - if info.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all, we have to deal with them for now. - self.instantiate_canonical_var(cause.span, info, |u| { - universe_map[u.as_usize()] - }) - } else if info.is_existential() { - match opt_values[BoundVar::new(index)] { - Some(k) => k, - None => self.instantiate_canonical_var(cause.span, info, |u| { - universe_map[u.as_usize()] - }), + let mut var_values = Vec::new(); + for (index, info) in query_response.variables.iter().enumerate() { + let value = if info.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all, we have to deal with them for now. + self.instantiate_canonical_var(cause.span, info, &var_values, |u| { + universe_map[u.as_usize()] + }) + } else if info.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // We need to still make sure to register any subtype relations returned by the + // query. + match opt_values[BoundVar::new(index)] { + Some(v) => { + if let CanonicalVarKind::Ty { universe: _, sub_root } = info.kind { + if let Some(prev) = var_values.get(sub_root.as_usize()) { + let &ty::Infer(ty::TyVar(vid)) = v.expect_ty().kind() else { + unreachable!("expected `sub_root` to be an inference variable"); + }; + let &ty::Infer(ty::TyVar(sub_root)) = prev.expect_ty().kind() + else { + unreachable!("expected `sub_root` to be an inference variable"); + }; + self.inner.borrow_mut().type_variables().sub(vid, sub_root); + } } - } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - opt_values[BoundVar::new(index)].expect( - "expected placeholder to be unified with itself during response", - ) + v } - }), - ), - }; + None => self.instantiate_canonical_var(cause.span, info, &var_values, |u| { + universe_map[u.as_usize()] + }), + } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + opt_values[BoundVar::new(index)] + .expect("expected placeholder to be unified with itself during response") + }; + var_values.push(value) + } + + let result_args = CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) }; let mut obligations = PredicateObligations::new(); diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 847715c6b3d3f..f34bee4333f6a 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -58,6 +58,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { self.root_var(var) } + fn sub_root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { + self.sub_root_var(var) + } fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { self.root_const_var(var) diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index bac5767c457ff..5097f9ec545a5 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -52,6 +52,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { variables: &'a mut Vec, primitive_var_infos: Vec>, variable_lookup_table: HashMap, + sub_root_lookup_table: HashMap, binder_index: ty::DebruijnIndex, /// We only use the debruijn index during lookup. We don't need to @@ -73,6 +74,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables, variable_lookup_table: Default::default(), + sub_root_lookup_table: Default::default(), primitive_var_infos: Vec::new(), binder_index: ty::INNERMOST, @@ -106,6 +108,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables, variable_lookup_table: Default::default(), + sub_root_lookup_table: Default::default(), primitive_var_infos: Vec::new(), binder_index: ty::INNERMOST, @@ -123,6 +126,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // We're able to reuse the `variable_lookup_table` as whether or not // it already contains an entry for `'static` does not matter. variable_lookup_table: env_canonicalizer.variable_lookup_table, + sub_root_lookup_table: Default::default(), primitive_var_infos: env_canonicalizer.primitive_var_infos, binder_index: ty::INNERMOST, @@ -177,6 +181,13 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { ty::BoundVar::from(idx) } + fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar { + let root_vid = self.delegate.sub_root_ty_var(vid); + let idx = + *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); + ty::BoundVar::from(idx) + } + fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) { let mut var_infos = self.primitive_var_infos; // See the rustc-dev-guide section about how we deal with universes @@ -323,11 +334,12 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { "ty vid should have been resolved fully before canonicalization" ); - CanonicalVarKind::Ty( - self.delegate - .universe_of_ty(vid) - .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")), - ) + let universe = self + .delegate + .universe_of_ty(vid) + .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")); + let sub_root = self.get_or_insert_sub_root(vid); + CanonicalVarKind::Ty { universe, sub_root } } ty::IntVar(vid) => { assert_eq!( diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 90a7c2e9f7879..fc66554d62f95 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -55,6 +55,7 @@ pub trait SolverDelegate: Deref + Sized { &self, cv_info: ty::CanonicalVarInfo, span: ::Span, + var_values: &[::GenericArg], universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ::GenericArg; fn add_item_bounds_for_hidden_type( 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 36f68808a2c8c..c1f0297669c3a 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 @@ -15,7 +15,8 @@ use rustc_index::IndexVec; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::{ - self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, + self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, + TypeFoldable, }; use tracing::{debug, instrument, trace}; @@ -356,37 +357,51 @@ where } } - let var_values = delegate.cx().mk_args_from_iter( - response.variables.iter().enumerate().map(|(index, info)| { - if info.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all (see the FIXME at the start of this method), we have to deal with - // them for now. - delegate.instantiate_canonical_var_with_infer(info, span, |idx| { - prev_universe + idx.index() - }) - } else if info.is_existential() { - // As an optimization we sometimes avoid creating a new inference variable here. - // - // All new inference variables we create start out in the current universe of the caller. - // This is conceptually wrong as these inference variables would be able to name - // more placeholders then they should be able to. However the inference variables have - // to "come from somewhere", so by equating them with the original values of the caller - // later on, we pull them down into their correct universe again. - if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { - v - } else { - delegate.instantiate_canonical_var_with_infer(info, span, |_| prev_universe) + let mut var_values = Vec::new(); + for (index, info) in response.variables.iter().enumerate() { + let value = if info.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + delegate.instantiate_canonical_var_with_infer(info, span, &var_values, |idx| { + prev_universe + idx.index() + }) + } else if info.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // We need to still make sure to register any subtype relations returned by the + // query. + if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { + if let CanonicalVarKind::Ty { universe: _, sub_root } = info.kind { + if let Some(prev) = var_values.get(sub_root.as_usize()) { + let ty::Infer(ty::TyVar(vid)) = v.expect_ty().kind() else { + unreachable!("expected `sub_root` to be an inference variable"); + }; + let ty::Infer(ty::TyVar(sub_root)) = prev.expect_ty().kind() else { + unreachable!("expected `sub_root` to be an inference variable"); + }; + delegate.sub_ty_vids_raw(vid, sub_root); + } } + v } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - original_values[info.expect_placeholder_index()] + // All new inference variables we create start out in the current universe + // of the caller. This is conceptually wrong as these inference variables + // would be able to name more placeholders then they should be able to. + // However the inference variables have to "come from somewhere", so by + // equating them with the original values of the caller later on, we pull + // them down into their correct universe again. + delegate.instantiate_canonical_var_with_infer(info, span, &var_values, |_| { + prev_universe + }) } - }), - ); - - CanonicalVarValues { var_values } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[info.expect_placeholder_index()] + }; + var_values.push(value) + } + CanonicalVarValues { var_values: delegate.cx().mk_args(&var_values) } } /// Unify the `original_values` with the `var_values` returned by the canonical query.. diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 3601c2cba9b55..14489cf39ac98 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -144,9 +144,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< &self, cv_info: CanonicalVarInfo<'tcx>, span: Span, + var_values: &[ty::GenericArg<'tcx>], universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ty::GenericArg<'tcx> { - self.0.instantiate_canonical_var(span, cv_info, universe_map) + self.0.instantiate_canonical_var(span, cv_info, var_values, universe_map) } fn add_item_bounds_for_hidden_type( diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 9bf16e1a94143..b65547ba04b8a 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -110,7 +110,7 @@ impl CanonicalVarInfo { pub fn is_existential(&self) -> bool { match self.kind { - CanonicalVarKind::Ty(_) | CanonicalVarKind::Int | CanonicalVarKind::Float => true, + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float => true, CanonicalVarKind::PlaceholderTy(_) => false, CanonicalVarKind::Region(_) => true, CanonicalVarKind::PlaceholderRegion(..) => false, @@ -122,7 +122,7 @@ impl CanonicalVarInfo { pub fn is_region(&self) -> bool { match self.kind { CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, - CanonicalVarKind::Ty(_) + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::PlaceholderTy(_) @@ -133,7 +133,7 @@ impl CanonicalVarInfo { pub fn expect_placeholder_index(self) -> usize { match self.kind { - CanonicalVarKind::Ty(_) + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::Region(_) @@ -158,7 +158,11 @@ impl CanonicalVarInfo { )] pub enum CanonicalVarKind { /// A general type variable `?T` that can be unified with arbitrary types. - Ty(UniverseIndex), + /// + /// We also store the index of the first type variable which is sub-unified + /// with this one. If there is no inference variable related to this one, + /// its `sub_root` just points to itself. + Ty { universe: UniverseIndex, sub_root: ty::BoundVar }, /// Integral type variable `?I` (that can only be unified with integral types). Int, @@ -187,7 +191,7 @@ pub enum CanonicalVarKind { impl CanonicalVarKind { pub fn universe(self) -> UniverseIndex { match self { - CanonicalVarKind::Ty(ui) => ui, + CanonicalVarKind::Ty { universe, sub_root: _ } => universe, CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::Const(ui) => ui, CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe(), @@ -203,7 +207,9 @@ impl CanonicalVarKind { /// the updated universe is not the root. pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarKind { match self { - CanonicalVarKind::Ty(_) => CanonicalVarKind::Ty(ui), + CanonicalVarKind::Ty { universe: _, sub_root } => { + CanonicalVarKind::Ty { universe: ui, sub_root } + } CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui), CanonicalVarKind::Const(_) => CanonicalVarKind::Const(ui), @@ -297,7 +303,7 @@ impl CanonicalVarValues { var_values: cx.mk_args_from_iter(infos.iter().enumerate().map( |(i, info)| -> I::GenericArg { match info.kind { - CanonicalVarKind::Ty(_) + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::PlaceholderTy(_) => { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 06a054ebdb491..3eb9c21b6008d 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -151,6 +151,7 @@ pub trait InferCtxtLike: Sized { fn universe_of_ct(&self, ct: ty::ConstVid) -> Option; fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; + fn sub_root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> ::Ty; From d18d85fe3769cf960d5919f6a38af53add868cd3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 28 Apr 2025 20:14:21 +0000 Subject: [PATCH 08/17] using `opt_values` may mean we don't have infer vars --- .../src/solve/eval_ctxt/canonical.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 c1f0297669c3a..4c3d202505a73 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 @@ -373,13 +373,14 @@ where if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { if let CanonicalVarKind::Ty { universe: _, sub_root } = info.kind { if let Some(prev) = var_values.get(sub_root.as_usize()) { - let ty::Infer(ty::TyVar(vid)) = v.expect_ty().kind() else { - unreachable!("expected `sub_root` to be an inference variable"); - }; - let ty::Infer(ty::TyVar(sub_root)) = prev.expect_ty().kind() else { - unreachable!("expected `sub_root` to be an inference variable"); - }; - delegate.sub_ty_vids_raw(vid, sub_root); + let v = delegate.shallow_resolve(v.expect_ty()); + let prev = delegate.shallow_resolve(prev.expect_ty()); + match (v.kind(), prev.kind()) { + (ty::Infer(ty::TyVar(vid)), ty::Infer(ty::TyVar(sub_root))) => { + delegate.sub_ty_vids_raw(vid, sub_root) + } + _ => {} + } } } v From 05e9bd568e2ab21017d0066ad1d6410b8ae7e321 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 30 Apr 2025 01:26:06 +0000 Subject: [PATCH 09/17] Incompletely allow overloaded call from opaque when self type bottoms out in infer --- compiler/rustc_hir_typeck/src/callee.rs | 158 ++++++++++++++++++------ compiler/rustc_infer/src/infer/mod.rs | 17 +++ 2 files changed, 140 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index f555d116c52db..107b1f1ae8dc2 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -66,7 +66,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let original_callee_ty = match &callee_expr.kind { + let expr_ty = match &callee_expr.kind { hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self .check_expr_with_expectation_and_args( callee_expr, @@ -76,8 +76,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.check_expr(callee_expr), }; - let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty); - let mut autoderef = self.autoderef(callee_expr.span, expr_ty); let mut result = None; while result.is_none() && autoderef.next().is_some() { @@ -144,7 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { autoderef: &Autoderef<'a, 'tcx>, ) -> Option> { let adjusted_ty = - self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); + self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty(false)); // If the callee is a bare function or a closure, then we're all set. match *adjusted_ty.kind() { @@ -241,6 +239,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } + // We only want to confirm a call step here if the infer var + // originated from the defining use of an opaque. + ty::Infer(ty::TyVar(vid)) + if let Some(alias_ty) = self.find_sup_as_registered_opaque(vid) => + { + return self + .try_overloaded_call_traits_for_alias(call_expr, alias_ty, arg_exprs) + .map(|(autoref, method)| { + let mut adjustments = self.adjust_steps(autoderef); + adjustments.extend(autoref); + self.apply_adjustments(callee_expr, adjustments); + CallStep::Overloaded(method) + }); + } + + ty::Infer(_) => { + return None; + } + ty::Error(_) => { return None; } @@ -305,40 +322,111 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Try the options that are least restrictive on the caller first. for (opt_trait_def_id, method_name, borrow) in call_trait_choices { - let Some(trait_def_id) = opt_trait_def_id else { continue }; + if let Some(confirmed) = self.try_overloaded_call_trait( + call_expr, + adjusted_ty, + opt_arg_exprs, + opt_trait_def_id, + method_name, + borrow, + ) { + return Some(confirmed); + } + } + + None + } + + fn try_overloaded_call_trait( + &self, + call_expr: &hir::Expr<'_>, + call_ty: Ty<'tcx>, + opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>, + opt_trait_def_id: Option, + method_name: Symbol, + borrow: bool, + ) -> Option<(Option>, MethodCallee<'tcx>)> { + let Some(trait_def_id) = opt_trait_def_id else { + return None; + }; + + let opt_input_type = opt_arg_exprs.map(|arg_exprs| { + Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span))) + }); + + let Some(ok) = self.lookup_method_for_operator( + self.misc(call_expr.span), + method_name, + trait_def_id, + call_ty, + opt_input_type, + ) else { + return None; + }; + let method = self.register_infer_ok_obligations(ok); + let mut autoref = None; + if borrow { + // Check for &self vs &mut self in the method signature. Since this is either + // the Fn or FnMut trait, it should be one of those. + let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else { + bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut") + }; - let opt_input_type = opt_arg_exprs.map(|arg_exprs| { - Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span))) + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded function call ops. + let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No); + + autoref = Some(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), + target: method.sig.inputs()[0], }); + } - if let Some(ok) = self.lookup_method_for_operator( - self.misc(call_expr.span), - method_name, - trait_def_id, - adjusted_ty, - opt_input_type, - ) { - let method = self.register_infer_ok_obligations(ok); - let mut autoref = None; - if borrow { - // Check for &self vs &mut self in the method signature. Since this is either - // the Fn or FnMut trait, it should be one of those. - let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else { - bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut") - }; - - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No); - - autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), - target: method.sig.inputs()[0], - }); - } + Some((autoref, method)) + } - return Some((autoref, method)); + fn try_overloaded_call_traits_for_alias( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + arg_exprs: &'tcx [rustc_hir::Expr<'tcx>], + ) -> Option<(Option>, MethodCallee<'tcx>)> { + let call_ty = alias_ty.to_ty(self.tcx); + + let call_traits = [ + (self.tcx.lang_items().fn_trait(), sym::call, true), + (self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true), + (self.tcx.lang_items().fn_once_trait(), sym::call_once, false), + (self.tcx.lang_items().async_fn_trait(), sym::async_call, true), + (self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true), + (self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false), + ]; + // We only want to try a call trait if it shows up in the bounds + // of the opaque. We confirm the first one that shows up in the + // bounds list, which can lead to inference weirdness but doesn't + // matter today. + for clause in + self.tcx.item_self_bounds(alias_ty.def_id).iter_instantiated(self.tcx, alias_ty.args) + { + let Some(poly_trait_ref) = clause.as_trait_clause() else { + continue; + }; + + if let Some(&(opt_trait_def_id, method_name, borrow)) = + call_traits.iter().find(|(trait_def_id, _, _)| { + trait_def_id.is_some_and(|trait_def_id| trait_def_id == poly_trait_ref.def_id()) + }) + && let Some(confirmed) = self.try_overloaded_call_trait( + call_expr, + call_ty, + Some(arg_exprs), + opt_trait_def_id, + method_name, + borrow, + ) + { + return Some(confirmed); } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 07e290349d60a..bc50fc421b2b6 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -959,6 +959,23 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() } + pub fn find_sup_as_registered_opaque(&self, ty_vid: TyVid) -> Option> { + let ty_sub_vid = self.sub_root_var(ty_vid); + let opaques: Vec<_> = self.inner.borrow_mut().opaque_types().iter_opaque_types().collect(); + opaques + .into_iter() + .find(|(_, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() + && self.sub_root_var(hidden_vid) == ty_sub_vid + { + true + } else { + false + } + }) + .map(|(key, _)| ty::AliasTy::new_from_args(self.tcx, key.def_id.to_def_id(), key.args)) + } + #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); From a7fa48b2d7c5ec942492dfbeca0a198cdaf92725 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 28 Apr 2025 18:16:34 +0000 Subject: [PATCH 10/17] Incompletely prefer opaque type bounds when self type bottoms out in infer --- .../src/solve/assembly/mod.rs | 63 +++++++++++++++++-- .../src/solve/eval_ctxt/mod.rs | 20 ++++++ 2 files changed, 78 insertions(+), 5 deletions(-) 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 542e212e1bfb1..df783a1294405 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -9,10 +9,10 @@ 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, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, - TypeVisitor, TypingMode, Upcast as _, elaborate, + self as ty, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate, }; -use tracing::{debug, instrument}; +use tracing::instrument; use super::trait_goals::TraitGoalProvenVia; use super::{has_only_region_constraints, inspect}; @@ -368,8 +368,7 @@ where }; if normalized_self_ty.is_ty_var() { - debug!("self type has been normalized to infer"); - return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect(); + return self.try_assemble_bounds_via_registered_opaque(goal, normalized_self_ty); } let goal: Goal = @@ -874,6 +873,60 @@ where } } + fn try_assemble_bounds_via_registered_opaque>( + &mut self, + goal: Goal, + self_ty: I::Ty, + ) -> Vec> { + //println!("for goal {goal:#?} and {self_ty:?}, we found an alias: {:#?}", self.find_sup_as_registered_opaque(self_ty)); + + let Some(alias_ty) = self.find_sup_as_registered_opaque(self_ty) else { + return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect(); + }; + + let mut candidates = vec![]; + for item_bound in + self.cx().item_self_bounds(alias_ty.def_id).iter_instantiated(self.cx(), alias_ty.args) + { + // TODO: comment + let assumption = + item_bound.fold_with(&mut ReplaceOpaque { cx: self.cx(), alias_ty, self_ty }); + candidates.extend(G::probe_and_match_goal_against_assumption( + self, + CandidateSource::AliasBound, + goal, + assumption, + |ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), + )); + } + + struct ReplaceOpaque { + cx: I, + alias_ty: ty::AliasTy, + self_ty: I::Ty, + } + impl TypeFolder for ReplaceOpaque { + fn cx(&self) -> I { + self.cx + } + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() { + if alias_ty == self.alias_ty { + return self.self_ty; + } + } + ty.super_fold_with(self) + } + } + + // TODO: + if candidates.is_empty() { + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + } + + candidates + } + /// Assemble and merge candidates for goals which are related to an underlying trait /// goal. Right now, this is normalizes-to and host effect 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 40388a480d0dc..8fa1f5dd2267f 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 @@ -1084,6 +1084,26 @@ where ) -> Result { self.delegate.is_transmutable(dst, src, assume) } + + pub(crate) fn find_sup_as_registered_opaque(&self, self_ty: I::Ty) -> Option> { + self.delegate + .clone_opaque_types_lookup_table() + .into_iter() + .chain(self.delegate.clone_duplicate_opaque_types()) + .find(|(_, hidden_ty)| { + if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() { + if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() { + if self.delegate.sub_root_ty_var(self_vid) + == self.delegate.sub_root_ty_var(hidden_vid) + { + return true; + } + } + } + false + }) + .map(|(key, _)| ty::AliasTy::new_from_args(self.cx(), key.def_id.into(), key.args)) + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` From 3a5a0bdc46d9cd8dd432def7d9f65ce91181d535 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 26 Nov 2024 16:22:33 +0100 Subject: [PATCH 11/17] add alias-relate fast path optimization --- .../src/solve/alias_relate.rs | 146 +++++++++++++++++- .../const-in-impl-fn-return-type.next.stderr | 19 ++- .../const-in-impl-fn-return-type.rs | 1 + 3 files changed, 162 insertions(+), 4 deletions(-) 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 f7bd460094328..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,13 +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::solve::GoalSource; -use rustc_type_ir::{self as ty, Interner}; +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 @@ -47,6 +55,12 @@ 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); @@ -113,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/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 } } From b85bf1e4257b9d82c2bdfd0da678b64a6a4e39d4 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 9 Apr 2025 21:35:43 +0200 Subject: [PATCH 12/17] i hate this :< --- compiler/rustc_hir_typeck/src/expr.rs | 2 +- .../rustc_hir_typeck/src/method/confirm.rs | 2 +- compiler/rustc_hir_typeck/src/method/probe.rs | 206 +++++++++++------- compiler/rustc_middle/src/traits/query.rs | 1 + ...tion-with-leaking-placeholders.next.stderr | 2 +- .../call_method_ambiguous.next.stderr | 2 +- .../call_method_on_inherent_impl.next.stderr | 2 +- ...ll_method_on_inherent_impl_ref.next.stderr | 2 +- .../hidden-type-is-opaque-2.default.stderr | 4 +- .../hidden-type-is-opaque-2.next.stderr | 4 +- .../impl-trait/method-resolution4.next.stderr | 9 - tests/ui/impl-trait/method-resolution4.rs | 3 +- .../recursive-bound-eval.next.stderr | 4 +- ...ncompat-call-after-qualified-path-0.stderr | 2 +- ...ncompat-call-after-qualified-path-1.stderr | 2 +- tests/ui/issues/issue-2151.stderr | 2 +- .../branches3.stderr | 8 +- ...inference-side-effects-are-lazy.gai.stderr | 2 +- ...ue-42234-unknown-receiver-type.full.stderr | 4 +- ...4-unknown-receiver-type.generic_arg.stderr | 4 +- .../closures_in_branches.stderr | 4 +- ...ution_trait_method_from_opaque.next.stderr | 6 +- tests/ui/typeck/issue-13853.rs | 2 +- tests/ui/typeck/issue-13853.stderr | 12 +- 24 files changed, 166 insertions(+), 125 deletions(-) delete mode 100644 tests/ui/impl-trait/method-resolution4.next.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2c28ffd1fe3df..a1e63b5986889 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1591,7 +1591,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); match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) { Ok(method) => { 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 6090c0f9aee22..2b99178d5cd29 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -11,7 +11,7 @@ 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::infer::{self, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::middle::stability; use rustc_middle::query::Providers; @@ -443,7 +443,17 @@ 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, 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 { @@ -469,13 +479,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(_)) => { @@ -583,60 +586,75 @@ 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): (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(), + ) + } 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(), + ) + }; 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, 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, _) => { @@ -669,6 +687,20 @@ 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>, final_ty: Ty<'tcx>) -> bool { + // nyaaaa~ + if infcx.next_trait_solver() { + let &ty::Infer(ty::TyVar(vid)) = final_ty.kind() else { + return false; + }; + infcx.find_sup_as_registered_opaque(vid).is_some() + } else { + false + } +} + impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn new( fcx: &'a FnCtxt<'a, 'tcx>, @@ -1882,31 +1914,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_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 3f6faa1a572d9..ff64fa36d166b 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/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/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/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/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-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`. From c8001d3649c7a74fa815aecd05d56578d1516c28 Mon Sep 17 00:00:00 2001 From: lcnr Date: Sat, 3 May 2025 22:00:58 +0000 Subject: [PATCH 13/17] double recursion_depth --- .../src/solve/eval_ctxt/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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 8fa1f5dd2267f..eb9f0d93d9a93 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 @@ -188,9 +188,13 @@ where generate_proof_tree: GenerateProofTree, span: I::Span, ) -> (Result<(HasChanged, Certainty), NoSolution>, Option>) { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, span, |ecx| { - ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) - }) + EvalCtxt::enter_root( + self, + self.cx().recursion_limit() * 2, + generate_proof_tree, + span, + |ecx| ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal), + ) } fn root_goal_may_hold_with_depth( @@ -218,7 +222,7 @@ where ) { EvalCtxt::enter_root( self, - self.cx().recursion_limit(), + self.cx().recursion_limit() * 2, generate_proof_tree, I::Span::dummy(), |ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal), From da1082b0da926de55176f6d643743905dd45c5e9 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 5 May 2025 18:35:18 +0000 Subject: [PATCH 14/17] blanket impls uwu --- compiler/rustc_hir_typeck/src/writeback.rs | 6 ++++-- compiler/rustc_middle/src/ty/context.rs | 6 +++++- .../src/solve/assembly/mod.rs | 20 +++++++++++++++++++ compiler/rustc_type_ir/src/interner.rs | 1 + 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 5ebbc6d6cc117..600622aa859b2 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -958,8 +958,10 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - debug_assert!(!r.is_bound(), "Should not be resolving bound region."); - self.fcx.tcx.lifetimes.re_erased + match r.kind() { + ty::ReBound(..) => r, + _ => self.fcx.tcx.lifetimes.re_erased, + } } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e835a9c943a50..04f8f2f275c6f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -589,7 +589,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), } - let trait_impls = tcx.trait_impls_of(trait_def_id); + #[allow(rustc::usage_of_type_ir_traits)] + self.for_each_blanket_impl(trait_def_id, f) + } + fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) { + let trait_impls = self.trait_impls_of(trait_def_id); for &impl_def_id in trait_impls.blanket_impls() { f(impl_def_id); } 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 df783a1294405..2628a3c04e2a5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -885,6 +885,26 @@ where }; let mut candidates = vec![]; + + let cx = self.cx(); + cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if cx.impl_is_default(impl_def_id) { + return; + } + + match G::consider_impl_candidate(self, goal, impl_def_id) { + Ok(mut candidate) => { + candidate.result.value.certainty = + candidate.result.value.certainty.and(Certainty::AMBIGUOUS); + candidates.push(candidate); + } + Err(NoSolution) => (), + } + }); + for item_bound in self.cx().item_self_bounds(alias_ty.def_id).iter_instantiated(self.cx(), alias_ty.args) { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 0fd2d9f3ad38b..75cc8fe6602e1 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -290,6 +290,7 @@ pub trait Interner: self_ty: Self::Ty, f: impl FnMut(Self::DefId), ); + fn for_each_blanket_impl(self, trait_def_id: Self::DefId, f: impl FnMut(Self::DefId)); fn has_item_definition(self, def_id: Self::DefId) -> bool; From 56eafa8dee2947e5d384e551df80d1143c3a9030 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 5 May 2025 18:59:05 +0000 Subject: [PATCH 15/17] enable `-Znext-solver` by default --- compiler/rustc_session/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 144aeb5c369cc..5f658f204bbba 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1003,7 +1003,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)] From 64876513de54350969b2e15f4089ee00b12e711f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 6 May 2025 15:07:05 +0000 Subject: [PATCH 16/17] w --- compiler/rustc_ast_passes/src/feature_gate.rs | 14 +++++++++----- compiler/rustc_middle/src/ty/context.rs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 915613a391374..1a6e8b85537e5 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,6 +1,7 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{NodeId, PatKind, attr, token}; +use rustc_errors::E0001; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_warn}; @@ -648,10 +649,13 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .map(|feat| feat.attr_sp) { #[allow(rustc::symbol_intern_string_literal)] - sess.dcx().emit_err(errors::IncompatibleFeatures { - spans: vec![gce_span], - f1: Symbol::intern("-Znext-solver=globally"), - f2: sym::generic_const_exprs, - }); + sess.dcx() + .create_fatal(errors::IncompatibleFeatures { + spans: vec![gce_span], + f1: Symbol::intern("-Znext-solver=globally"), + f2: sym::generic_const_exprs, + }) + .with_code(E0001) + .emit(); } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 04f8f2f275c6f..2e773f528d6d1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3337,7 +3337,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn next_trait_solver_globally(self) -> bool { - self.sess.opts.unstable_opts.next_solver.globally + self.sess.opts.unstable_opts.next_solver.globally && !self.features().generic_const_exprs() } pub fn next_trait_solver_in_coherence(self) -> bool { From 363e6468c093d46f4b1d651df8075350dcfecbba Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 7 May 2025 03:10:26 +0000 Subject: [PATCH 17/17] to span_delayed_bug --- compiler/rustc_borrowck/src/region_infer/opaque_types.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index f44ff1fe5672c..5874855d3ffb9 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -224,12 +224,11 @@ pub(crate) fn handle_opaque_type_uses<'tcx>( &opaque_types, ); - if let Some((key, hidden_type)) = infcx + for (key, hidden_type) in infcx .inner .borrow_mut() .opaque_types() .opaque_types_added_since(opaque_types_storage_num_entries) - .next() { let opaque_type_string = tcx.def_path_str(key.def_id); let msg = format!("unexpected cyclic definition of `{opaque_type_string}`");