diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 01d0c8483ac54..4fb2d0adffa14 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -74,7 +74,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { // opaque type and instead return `None` in `fn overloaded_deref_ty` if the // opaque does not have a `Deref` item-bound. if let &ty::Infer(ty::TyVar(vid)) = self.state.cur_ty.kind() - && !self.infcx.has_opaques_with_sub_unified_hidden_type(vid) + && !self.infcx.has_hidden_types_of_opaques_modulo_sub_unification(vid) { return None; } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d5bc834b831c7..ced69866f2737 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -425,8 +425,11 @@ fn check_opaque_meets_bounds<'tcx>( let _ = infcx.take_opaque_types(); Ok(()) } else { + let (opaques, hiddens) = infcx.take_opaque_types(); + // We don't track anything on `hidden_types_of_opaques` in the old solver. + assert!(hiddens.is_empty()); // Check that any hidden types found during wf checking match the hidden types that `type_of` sees. - for (mut key, mut ty) in infcx.take_opaque_types() { + for (mut key, mut ty) in opaques { ty.ty = infcx.resolve_vars_if_possible(ty.ty); key = infcx.resolve_vars_if_possible(key); sanity_check_found_hidden_type(tcx, key, ty)?; @@ -2311,9 +2314,12 @@ pub(super) fn check_coroutine_obligations( } if !tcx.next_trait_solver_globally() { + let (opaques, hiddens) = infcx.take_opaque_types(); + // We don't track anything on `hidden_types_of_opaques` in the old solver. + assert!(hiddens.is_empty()); // Check that any hidden types found when checking these stalled coroutine obligations // are valid. - for (key, ty) in infcx.take_opaque_types() { + for (key, ty) in opaques { let hidden_type = infcx.resolve_vars_if_possible(ty); let key = infcx.resolve_vars_if_possible(key); sanity_check_found_hidden_type(tcx, key, hidden_type)?; diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index e250ec4c7af40..7b165363e525e 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -335,7 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Infer(ty::TyVar(vid)) => { // If we end up with an inference variable which is not the hidden type of // an opaque, emit an error. - if !self.has_opaques_with_sub_unified_hidden_type(vid) { + if !self.has_hidden_types_of_opaques_modulo_sub_unification(vid) { self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty); return None; } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 40bf435e6d110..9f40b8d0de39c 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -419,7 +419,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { ty::List::empty() }; - let value = query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty }; + let opaque_hidden_ty_bounds_in_body = if self.next_trait_solver() { + self.tcx.mk_opaque_hidden_ty_bounds_in_body_from_iter( + self.inner.borrow_mut().opaque_types().iter_opaque_hidden_type_bounds(), + ) + } else { + ty::List::empty() + }; + let value = query::MethodAutoderefSteps { + predefined_opaques_in_body, + opaque_hidden_ty_bounds_in_body, + self_ty, + }; let query_input = self .canonicalize_query(ParamEnvAnd { param_env: self.param_env, value }, &mut orig_values); @@ -434,7 +445,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let infcx = &self.infcx; let (ParamEnvAnd { param_env: _, value }, var_values) = infcx.instantiate_canonical(span, &query_input.canonical); - let query::MethodAutoderefSteps { predefined_opaques_in_body: _, self_ty } = value; + let query::MethodAutoderefSteps { + predefined_opaques_in_body: _, + opaque_hidden_ty_bounds_in_body: _, + self_ty, + } = value; debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); let prev_opaque_entries = self.inner.borrow_mut().opaque_types().num_entries(); MethodAutoderefStepsResult { @@ -444,7 +459,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, prev_opaque_entries, ), - self_ty_is_opaque: false, + self_ty_is_hidden_ty_of_opaque: false, autoderefs: 0, from_unsafe_deref: false, unsize: false, @@ -632,7 +647,12 @@ pub(crate) fn method_autoderef_steps<'tcx>( let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); let ParamEnvAnd { param_env, - value: query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty }, + value: + query::MethodAutoderefSteps { + predefined_opaques_in_body, + opaque_hidden_ty_bounds_in_body, + self_ty, + }, } = goal; for (key, ty) in predefined_opaques_in_body { let prev = infcx @@ -652,14 +672,15 @@ pub(crate) fn method_autoderef_steps<'tcx>( debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`"); } } + infcx.add_opaque_hidden_type_bounds_in_storage(opaque_hidden_ty_bounds_in_body); let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); // We accept not-yet-defined opaque types in the autoderef // chain to support recursive calls. We do error if the final // infer var is not an opaque. - let self_ty_is_opaque = |ty: Ty<'_>| { + let self_ty_is_hidden_ty_of_opaque = |ty: Ty<'_>| { if let &ty::Infer(ty::TyVar(vid)) = ty.kind() { - infcx.has_opaques_with_sub_unified_hidden_type(vid) + infcx.has_hidden_types_of_opaques_modulo_sub_unification(vid) } else { false } @@ -701,7 +722,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( ty, prev_opaque_entries, ), - self_ty_is_opaque: self_ty_is_opaque(ty), + self_ty_is_hidden_ty_of_opaque: self_ty_is_hidden_ty_of_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -725,7 +746,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( ty, prev_opaque_entries, ), - self_ty_is_opaque: self_ty_is_opaque(ty), + self_ty_is_hidden_ty_of_opaque: self_ty_is_hidden_ty_of_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -742,14 +763,16 @@ pub(crate) fn method_autoderef_steps<'tcx>( }; let final_ty = autoderef_via_deref.final_ty(); let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy { - reached_raw_pointer, - ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars, - final_ty, - prev_opaque_entries, - ), - }), + ty::Infer(ty::TyVar(_)) if !self_ty_is_hidden_ty_of_opaque(final_ty) => { + Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + final_ty, + prev_opaque_entries, + ), + }) + } ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx.make_query_response_ignoring_pending_obligations( @@ -766,7 +789,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( Ty::new_slice(infcx.tcx, *elem_ty), prev_opaque_entries, ), - self_ty_is_opaque: false, + self_ty_is_hidden_ty_of_opaque: false, autoderefs, // this could be from an unsafe deref if we had // a *mut/const [T; N] @@ -2293,10 +2316,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - // Check whether any opaque types in the autoderef chain have been + // Check whether any hidden type of opaque in the autoderef chain have been // constrained. for step in self.steps { - if step.self_ty_is_opaque { + if step.self_ty_is_hidden_ty_of_opaque { debug!(?step.autoderefs, ?step.self_ty, "self_type_is_opaque"); let constrained_opaque = self.probe(|_| { // If we fail to instantiate the self type of this diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index cd35af8be73cf..975d1931af749 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -160,6 +160,7 @@ impl<'tcx> InferCtxt<'tcx> { .borrow_mut() .opaque_type_storage .take_opaque_types() + .0 .map(|(k, v)| (k, v.ty)) .collect(); diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index f9b08efad88cf..5c0648ae54a44 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -340,30 +340,41 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries { self.inner.borrow_mut().opaque_types().num_entries() } + fn num_opaque_hidden_type_bounds(&self) -> usize { + self.inner.borrow_mut().opaque_types().num_opaque_hidden_type_bounds() + } fn clone_opaque_types_lookup_table(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect() } - fn clone_duplicate_opaque_types(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { + fn clone_opaque_hidden_ty_bounds(&self) -> Vec<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)> { + self.inner.borrow_mut().opaque_types().iter_opaque_hidden_type_bounds().collect() + } + fn clone_opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { self.inner .borrow_mut() .opaque_types() - .iter_duplicate_entries() + .opaque_types_added_since(prev_entries) .map(|(k, h)| (k, h.ty)) .collect() } - fn clone_opaque_types_added_since( + fn clone_opaque_hidden_ty_bounds_added_since( &self, prev_entries: OpaqueTypeStorageEntries, - ) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { + ) -> Vec<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)> { self.inner .borrow_mut() .opaque_types() - .opaque_types_added_since(prev_entries) - .map(|(k, h)| (k, h.ty)) + .opaque_hidden_ty_bounds_added_since(prev_entries) .collect() } - fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec> { - self.opaques_with_sub_unified_hidden_type(ty) + fn hidden_types_of_opaques_modulo_sub_unification( + &self, + ty_vid: ty::TyVid, + ) -> Vec<(Ty<'tcx>, Vec>)> { + self.hidden_types_of_opaques_modulo_sub_unification(ty_vid) } fn register_hidden_type_in_storage( @@ -388,6 +399,19 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { .opaque_types() .add_duplicate(opaque_type_key, ty::ProvisionalHiddenType { span, ty: hidden_ty }) } + fn add_hidden_type_of_opaque_in_storage( + &self, + hidden_ty: Ty<'tcx>, + bounds: impl IntoIterator>, + ) { + self.add_hidden_type_of_opaque_in_storage(hidden_ty, bounds); + } + fn add_opaque_hidden_ty_bounds_in_storage( + &self, + bounds: &[(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)], + ) { + self.add_opaque_hidden_type_bounds_in_storage(bounds); + } fn reset_opaque_types(&self) { let _ = self.take_opaque_types(); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 56fcc72bd9769..737127797942b 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -11,7 +11,7 @@ use region_constraints::{ GenericKind, RegionConstraintCollector, RegionConstraintStorage, VarInfos, VerifyBound, }; pub use relate::combine::PredicateEmittingRelation; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::unify::{self as ut, UnifyKey, UnifyValue}; @@ -1121,8 +1121,15 @@ impl<'tcx> InferCtxt<'tcx> { } #[instrument(level = "debug", skip(self), ret)] - pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)> { - self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() + pub fn take_opaque_types( + &self, + ) -> ( + Vec<(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)>, + Vec<(Ty<'tcx>, FxIndexSet>)>, + ) { + let mut inner = self.inner.borrow_mut(); + let (opaques, hiddens) = inner.opaque_type_storage.take_opaque_types(); + (opaques.collect(), hiddens.collect()) } #[instrument(level = "debug", skip(self), ret)] @@ -1130,7 +1137,7 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() } - pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool { + pub fn has_hidden_types_of_opaques_modulo_sub_unification(&self, ty_vid: TyVid) -> bool { if !self.next_trait_solver() { return false; } @@ -1138,8 +1145,8 @@ impl<'tcx> InferCtxt<'tcx> { let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); let inner = &mut *self.inner.borrow_mut(); let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); - inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| { - if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + inner.opaque_type_storage.iter_hidden_types_of_opaques().any(|(hidden_ty, _)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.kind() { let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); if opaque_sub_vid == ty_sub_vid { return true; @@ -1187,6 +1194,36 @@ impl<'tcx> InferCtxt<'tcx> { .collect() } + pub fn hidden_types_of_opaques_modulo_sub_unification( + &self, + ty_vid: TyVid, + ) -> Vec<(Ty<'tcx>, Vec>)> { + // Avoid accidentally allowing more code to compile with the old solver. + if !self.next_trait_solver() { + return vec![]; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + // This is iffy, can't call `type_variables()` as we're already + // borrowing the `opaque_type_storage` here. + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner + .opaque_type_storage + .iter_hidden_types_of_opaques() + .filter_map(|(hidden_ty, bounds)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return Some((hidden_ty, bounds.iter().copied().collect())); + } + } + + None + }) + .collect() + } + #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 70423ca7da1be..287009325eab8 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -208,6 +208,59 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty) } + pub fn add_hidden_type_of_opaque_in_storage( + &self, + hidden_ty: Ty<'tcx>, + bounds: impl IntoIterator>, + ) { + let ty::Infer(ty::TyVar(vid)) = *hidden_ty.kind() else { + return; + }; + if self.try_resolve_ty_var(vid).is_ok() { + return; + } + + let ty_sub_vid = self.sub_unification_table_root_var(vid); + let inner = &mut *self.inner.borrow_mut(); + // This is iffy, can't call `type_variables()` as we're already + // borrowing the `opaque_type_storage` here. + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + + // Since we lookup `hidden_types_of_opaques` modulo sub-roots, + // it's okay to save them with the preexisting key that + // sub-unified with the given `hidden_ty`. + // + // And doing so helps avoiding possibly duplicates (modulo sub roots) + // which is not so good for caching and goal evaluation progress + // heuristics. + let hidden_ty = inner + .opaque_type_storage + .iter_hidden_types_of_opaques() + .map(|(hidden_ty, _)| hidden_ty) + .find(|hidden_ty| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.kind() { + type_variables.sub_unification_table_root_var(hidden_vid) == ty_sub_vid + } else { + false + } + }) + .unwrap_or(hidden_ty); + + inner.opaque_types().add_hidden_type_of_opaque(hidden_ty, bounds); + } + + pub fn add_opaque_hidden_type_bounds_in_storage( + &self, + bounds: &[(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)], + ) { + for chunk in bounds.chunk_by(|a, b| a.0 == b.0) { + self.add_hidden_type_of_opaque_in_storage( + chunk[0].0, + chunk.iter().map(|(_, bound)| *bound), + ); + } + } + /// Insert a hidden type into the opaque type storage, equating it /// with any previous entries if necessary. /// diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index 066d12be320a2..bb5acbbe5593a 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -1,9 +1,11 @@ +use std::iter; use std::ops::Deref; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::indexmap::map::Entry; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; -use rustc_middle::ty::{OpaqueTypeKey, ProvisionalHiddenType, Ty}; +use rustc_middle::ty::{self as ty, OpaqueTypeKey, ProvisionalHiddenType, Ty}; use tracing::instrument; use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}; @@ -12,6 +14,9 @@ use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}; pub struct OpaqueTypeStorage<'tcx> { opaque_types: FxIndexMap, ProvisionalHiddenType<'tcx>>, duplicate_entries: Vec<(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)>, + // FIXME: document those two fields + hidden_types_of_opaques: FxIndexMap, FxIndexSet>>, + opaque_hidden_type_bounds: Vec<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)>, } /// The number of entries in the opaque type storage at a given point. @@ -22,11 +27,17 @@ pub struct OpaqueTypeStorage<'tcx> { pub struct OpaqueTypeStorageEntries { opaque_types: usize, duplicate_entries: usize, + opaque_hidden_type_bounds: usize, } impl rustc_type_ir::inherent::OpaqueTypeStorageEntries for OpaqueTypeStorageEntries { - fn needs_reevaluation(self, canonicalized: usize) -> bool { - self.opaque_types != canonicalized + fn needs_reevaluation(self, opaques: usize, hidden_ty_bounds: usize) -> bool { + let OpaqueTypeStorageEntries { + opaque_types, + duplicate_entries: _, + opaque_hidden_type_bounds, + } = self; + opaques != opaque_types || hidden_ty_bounds != opaque_hidden_type_bounds } } @@ -40,7 +51,6 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { if let Some(prev) = prev { *self.opaque_types.get_mut(&key).unwrap() = prev; } else { - // FIXME(#120456) - is `swap_remove` correct? match self.opaque_types.swap_remove(&key) { None => bug!("reverted opaque type inference that was never registered: {:?}", key), Some(_) => {} @@ -53,25 +63,77 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { assert!(entry.is_some()); } + pub(crate) fn truncate_hidden_types_of_opaques( + &mut self, + hidden_ty: Ty<'tcx>, + len: Option, + ) { + let removed = if let Some(len) = len { + let bounds = self.hidden_types_of_opaques.get_mut(&hidden_ty).unwrap(); + let removed = bounds.len() - len; + bounds.truncate(len); + removed + } else { + match self.hidden_types_of_opaques.swap_remove(&hidden_ty) { + None => bug!( + "reverted opaque hidden type inference that was never registered: {:?}", + hidden_ty + ), + Some(bounds) => bounds.len(), + } + }; + + let truncate_to = self.opaque_hidden_type_bounds.len() - removed; + debug_assert!( + (&self.opaque_hidden_type_bounds[truncate_to..]).iter().all(|(h, _)| *h == hidden_ty) + ); + self.opaque_hidden_type_bounds.truncate(truncate_to); + } + pub fn is_empty(&self) -> bool { - let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; - opaque_types.is_empty() && duplicate_entries.is_empty() + let OpaqueTypeStorage { + opaque_types, + duplicate_entries, + hidden_types_of_opaques, + opaque_hidden_type_bounds, + } = self; + opaque_types.is_empty() + && duplicate_entries.is_empty() + && hidden_types_of_opaques.is_empty() + && opaque_hidden_type_bounds.is_empty() } pub(crate) fn take_opaque_types( &mut self, - ) -> impl Iterator, ProvisionalHiddenType<'tcx>)> { - let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; - std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)) + ) -> ( + impl Iterator, ProvisionalHiddenType<'tcx>)>, + impl Iterator, FxIndexSet>)>, + ) { + let OpaqueTypeStorage { + opaque_types, + duplicate_entries, + hidden_types_of_opaques, + opaque_hidden_type_bounds, + } = self; + let _ = std::mem::take(opaque_hidden_type_bounds); + ( + std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)), + std::mem::take(hidden_types_of_opaques).into_iter(), + ) } pub fn num_entries(&self) -> OpaqueTypeStorageEntries { OpaqueTypeStorageEntries { opaque_types: self.opaque_types.len(), duplicate_entries: self.duplicate_entries.len(), + opaque_hidden_type_bounds: self.opaque_hidden_type_bounds.len(), } } + pub fn num_opaque_hidden_type_bounds(&self) -> usize { + self.opaque_hidden_type_bounds.len() + } + pub fn opaque_types_added_since( &self, prev_entries: OpaqueTypeStorageEntries, @@ -83,6 +145,12 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { .chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied()) } + pub fn opaque_hidden_ty_bounds_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> impl Iterator, ty::OpaqueHiddenTyBound<'tcx>)> { + self.opaque_hidden_type_bounds.iter().skip(prev_entries.opaque_hidden_type_bounds).copied() + } /// Only returns the opaque types from the lookup table. These are used /// when normalizing opaque types and have a unique key. /// @@ -108,10 +176,39 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { pub fn iter_opaque_types( &self, ) -> impl Iterator, ProvisionalHiddenType<'tcx>)> { - let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + let OpaqueTypeStorage { + opaque_types, + duplicate_entries, + hidden_types_of_opaques: _, + opaque_hidden_type_bounds: _, + } = self; opaque_types.iter().map(|(k, v)| (*k, *v)).chain(duplicate_entries.iter().copied()) } + pub fn iter_hidden_types_of_opaques( + &self, + ) -> impl Iterator, &FxIndexSet>)> { + let OpaqueTypeStorage { + opaque_types: _, + duplicate_entries: _, + hidden_types_of_opaques, + opaque_hidden_type_bounds: _, + } = self; + hidden_types_of_opaques.iter().map(|(hidden, bounds)| (*hidden, bounds)) + } + + pub fn iter_opaque_hidden_type_bounds( + &self, + ) -> impl Iterator, ty::OpaqueHiddenTyBound<'tcx>)> { + let OpaqueTypeStorage { + opaque_types: _, + duplicate_entries: _, + hidden_types_of_opaques: _, + opaque_hidden_type_bounds, + } = self; + opaque_hidden_type_bounds.iter().copied() + } + #[inline] pub(crate) fn with_log<'a>( &'a mut self, @@ -158,4 +255,41 @@ impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> { self.storage.duplicate_entries.push((key, hidden_type)); self.undo_log.push(UndoLog::DuplicateOpaqueType); } + + pub fn add_hidden_type_of_opaque( + &mut self, + hidden_ty: Ty<'tcx>, + bounds: impl IntoIterator>, + ) { + let OpaqueTypeStorage { + opaque_types: _, + duplicate_entries: _, + hidden_types_of_opaques, + opaque_hidden_type_bounds, + } = self.storage; + let prev_len = match hidden_types_of_opaques.entry(hidden_ty) { + Entry::Occupied(mut entry) => { + let entry = entry.get_mut(); + let len = entry.len(); + entry.extend(bounds); + if entry.len() == len { + return; + } + opaque_hidden_type_bounds + .extend(iter::repeat(hidden_ty).zip(entry.iter().skip(len).copied())); + Some(len) + } + Entry::Vacant(vacant) => { + let bounds: FxIndexSet<_> = bounds.into_iter().collect(); + if bounds.is_empty() { + return; + } + let entry = vacant.insert(bounds); + opaque_hidden_type_bounds + .extend(iter::repeat(hidden_ty).zip(entry.iter().copied())); + None + } + }; + self.undo_log.push(UndoLog::HiddenTypesOfOpaques(hidden_ty, prev_len)); + } } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 2b1ac29173483..a4e55945e819e 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; -use rustc_middle::ty::{self, OpaqueTypeKey, ProvisionalHiddenType}; +use rustc_middle::ty::{self, OpaqueTypeKey, ProvisionalHiddenType, Ty}; use tracing::debug; use crate::infer::unify_key::{ConstVidKey, RegionVidKey}; @@ -20,6 +20,7 @@ pub struct Snapshot<'tcx> { pub(crate) enum UndoLog<'tcx> { DuplicateOpaqueType, OpaqueTypes(OpaqueTypeKey<'tcx>, Option>), + HiddenTypesOfOpaques(Ty<'tcx>, Option), TypeVariables(type_variable::UndoLog<'tcx>), ConstUnificationTable(sv::UndoLog>>), IntUnificationTable(sv::UndoLog>), @@ -67,6 +68,9 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { match undo { UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(), UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx), + UndoLog::HiddenTypesOfOpaques(ty, len) => { + self.opaque_type_storage.truncate_hidden_types_of_opaques(ty, len) + } UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index f2a2c6c3f4a63..1fa9348821590 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -70,6 +70,7 @@ pub struct MethodAutoderefSteps<'tcx> { /// /// Only used by the new solver for now. pub predefined_opaques_in_body: solve::PredefinedOpaques<'tcx>, + pub opaque_hidden_ty_bounds_in_body: solve::OpaqueHiddenTyBounds<'tcx>, pub self_ty: Ty<'tcx>, } @@ -155,7 +156,7 @@ impl<'tcx> FromIterator> for DropckConstraint<'tcx> { #[derive(Debug, StableHash)] pub struct CandidateStep<'tcx> { pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, - pub self_ty_is_opaque: bool, + pub self_ty_is_hidden_ty_of_opaque: bool, pub autoderefs: usize, /// `true` if the type results from a dereference of a raw pointer. /// when assembling candidates, we include these steps, but not when diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 02f9ef365f288..abb0cb4528d73 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -21,6 +21,7 @@ pub type GoalStalledOnOpaques<'tcx> = ir::solve::GoalStalledOnOpaques = ir::solve::SucceededInErased>; pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>; +pub type OpaqueHiddenTyBounds<'tcx> = &'tcx ty::List<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)>; // Interning CanonicalInput drastically reduces max memory usage when compiling a crate that has // trait solver recursion depth overflows with next-solver deduplicating individual inputs. @@ -72,11 +73,8 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { Ok(FallibleTypeFolder::cx(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().try_fold_with(folder)?, - opaque_types: self - .opaque_types - .iter() - .map(|opaque| opaque.try_fold_with(folder)) - .collect::>()?, + opaque_types: self.opaque_types.try_fold_with(folder)?, + opaque_hidden_type_bounds: self.opaque_hidden_type_bounds.try_fold_with(folder)?, normalization_nested_goals: self .normalization_nested_goals .clone() @@ -94,7 +92,8 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { TypeFolder::cx(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().fold_with(folder), - opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), + opaque_types: self.opaque_types.fold_with(folder), + opaque_hidden_type_bounds: self.opaque_hidden_type_bounds.fold_with(folder), normalization_nested_goals: self.normalization_nested_goals.clone().fold_with(folder), }) } @@ -105,11 +104,13 @@ impl<'tcx> TypeVisitable> for ExternalConstraints<'tcx> { let ExternalConstraintsData { region_constraints, opaque_types, + opaque_hidden_type_bounds, normalization_nested_goals, } = &**self; try_visit!(region_constraints.visit_with(visitor)); try_visit!(opaque_types.visit_with(visitor)); + try_visit!(opaque_hidden_type_bounds.visit_with(visitor)); normalization_nested_goals.visit_with(visitor) } } @@ -121,6 +122,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start - static_assert_size!(GoalStalledOn<'_>, 56); + static_assert_size!(GoalStalledOn<'_>, 64); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5ff5c05de734a..7923c71a18c73 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -65,7 +65,7 @@ use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ CanonicalInput, CanonicalInputData, ExternalConstraints, ExternalConstraintsData, - PredefinedOpaques, + OpaqueHiddenTyBounds, PredefinedOpaques, }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ @@ -158,6 +158,8 @@ pub struct CtxtInterners<'tcx> { adt_def: InternedSet<'tcx, AdtDefData>, external_constraints: InternedSet<'tcx, ExternalConstraintsData>>, predefined_opaques_in_body: InternedSet<'tcx, List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>>, + hidden_types_of_opaques_in_body: + InternedSet<'tcx, List<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)>>, fields: InternedSet<'tcx, List>, local_def_ids: InternedSet<'tcx, List>, captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, @@ -197,6 +199,7 @@ impl<'tcx> CtxtInterners<'tcx> { adt_def: InternedSet::with_capacity(N), external_constraints: InternedSet::with_capacity(N), predefined_opaques_in_body: InternedSet::with_capacity(N), + hidden_types_of_opaques_in_body: InternedSet::with_capacity(N * 2), fields: InternedSet::with_capacity(N * 4), local_def_ids: InternedSet::with_capacity(N), captures: InternedSet::with_capacity(N), @@ -2042,6 +2045,7 @@ slice_interners!( patterns: pub mk_patterns(Pattern<'tcx>), outlives: pub mk_outlives(ty::ArgOutlivesClause<'tcx>), predefined_opaques_in_body: pub mk_predefined_opaques_in_body((ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)), + hidden_types_of_opaques_in_body: pub mk_opaque_hidden_ty_bounds_in_body((Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)), ); impl<'tcx> TyCtxt<'tcx> { @@ -2537,6 +2541,14 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_predefined_opaques_in_body(xs)) } + pub fn mk_opaque_hidden_ty_bounds_in_body_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>), OpaqueHiddenTyBounds<'tcx>>, + { + T::collect_and_apply(iter, |xs| self.mk_opaque_hidden_ty_bounds_in_body(xs)) + } + pub fn mk_clauses_from_iter(self, iter: I) -> T::Output where I: Iterator, diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 202991d3f0ada..fa1062b837c00 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -75,6 +75,16 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Self::PredefinedOpaques { self.mk_predefined_opaques_in_body(data) } + + type OpaqueHiddenTyBounds = solve::OpaqueHiddenTyBounds<'tcx>; + + fn mk_opaque_hidden_ty_bounds_in_body( + self, + data: &[(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)], + ) -> Self::OpaqueHiddenTyBounds { + self.mk_opaque_hidden_ty_bounds_in_body(data) + } + type LocalDefIds = &'tcx ty::List; type CanonicalVarKinds = CanonicalVarKinds<'tcx>; fn mk_canonical_var_kinds( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index cc6a8619e1e74..060c58da2da83 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -79,7 +79,7 @@ pub use self::fold::*; pub use self::instance::{Instance, InstanceKind, ReifyReason, ShimKind}; pub(crate) use self::list::RawList; pub use self::list::{List, ListWithCachedTypeInfo}; -pub use self::opaque_types::OpaqueTypeKey; +pub use self::opaque_types::{OpaqueHiddenTyBound, OpaqueTypeKey}; pub use self::pattern::{Pattern, PatternKind}; pub use self::predicate::{ AliasTerm, AliasTermKind, ArgOutlivesClause, Clause, ClauseKind, CoercePredicate, diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index bf716e8027a0a..737d3c826faad 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -10,6 +10,8 @@ use crate::ty::{ pub type OpaqueTypeKey<'tcx> = rustc_type_ir::OpaqueTypeKey>; +pub type OpaqueHiddenTyBound<'tcx> = rustc_type_ir::OpaqueHiddenTyBound>; + /// Converts generic params of a TypeFoldable from one /// item's generics to another. Usually from a function's generics /// list to the opaque type's own generics. diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index eb3a53d8fa968..44335543ee1cb 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -779,6 +779,7 @@ macro_rules! list_fold { list_fold! { &'tcx ty::List> : mk_poly_existential_predicates, &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>: mk_predefined_opaques_in_body, + &'tcx ty::List<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)>: mk_opaque_hidden_ty_bounds_in_body, &'tcx ty::List> : mk_place_elems, &'tcx ty::List> : mk_patterns, &'tcx ty::List> : mk_outlives, diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index cf36b1922b8c1..ec929fdc921b7 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -2,15 +2,16 @@ use std::collections::hash_map::Entry; use std::mem; use rustc_type_ir::inherent::*; -use rustc_type_ir::solve::{Goal, QueryInput}; +use rustc_type_ir::solve::{Certainty, ExternalConstraintsData, Goal, QueryInput, Response}; use rustc_type_ir::{ - self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, CanonicalizerState, - Flags, InferCtxtLike, Interner, PlaceholderConst, PlaceholderType, Region, TypeFlags, - TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, CanonicalVarValues, + CanonicalizerState, Flags, InferCtxtLike, Interner, PlaceholderConst, PlaceholderType, Region, + TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use thin_vec::ThinVec; use crate::delegate::SolverDelegate; +use crate::solve::RawExternalConstraintsData; /// Does this have infer/placeholder/param, free regions or ReErased? const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( @@ -95,6 +96,87 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { Canonical { max_universe, var_kinds, value } } + pub(super) fn canonicalize_query_response( + delegate: &'a D, + max_input_universe: ty::UniverseIndex, + var_values: CanonicalVarValues, + certainty: Certainty, + external_constraints: RawExternalConstraintsData, + ) -> ty::Canonical> { + let mut canonicalizer = + Canonicalizer::new(delegate, CanonicalizeMode::Response { max_input_universe }); + + let var_values = if var_values.has_type_flags(NEEDS_CANONICAL) { + var_values.fold_with(&mut canonicalizer) + } else { + var_values + }; + + let RawExternalConstraintsData { + region_constraints, + opaque_types, + opaque_hidden_type_bounds: mut opaque_hidden_type_bounds_candidates, + normalization_nested_goals, + } = external_constraints; + let region_constraints = if region_constraints.has_type_flags(NEEDS_CANONICAL) { + region_constraints.fold_with(&mut canonicalizer) + } else { + region_constraints + }; + let opaque_types = if opaque_types.has_type_flags(NEEDS_CANONICAL) { + opaque_types.fold_with(&mut canonicalizer) + } else { + opaque_types + }; + let normalization_nested_goals = + if normalization_nested_goals.has_type_flags(NEEDS_CANONICAL) { + normalization_nested_goals.fold_with(&mut canonicalizer) + } else { + normalization_nested_goals + }; + + // Filter out irrelevant hidden tys, in a fixed-point iteration to make them less bulky. + let mut opaque_hidden_type_bounds = vec![]; + while !opaque_hidden_type_bounds_candidates.is_empty() { + let prev_len = opaque_hidden_type_bounds.len(); + opaque_hidden_type_bounds_candidates.retain(|bounds @ (hidden_ty, _)| { + if let ty::Infer(ty::TyVar(vid)) = hidden_ty.kind() + && canonicalizer + .state + .sub_root_lookup_table + .contains_key(&delegate.sub_unification_table_root_var(vid)) + { + opaque_hidden_type_bounds.push(bounds.clone().fold_with(&mut canonicalizer)); + false + } else { + true + } + }); + if opaque_hidden_type_bounds.len() == prev_len { + break; + } + } + + let value = Response { + certainty, + var_values, + external_constraints: delegate.cx().mk_external_constraints(ExternalConstraintsData { + region_constraints, + opaque_types: delegate.cx().mk_predefined_opaques_in_body(&opaque_types), + opaque_hidden_type_bounds: delegate + .cx() + .mk_opaque_hidden_ty_bounds_in_body(&opaque_hidden_type_bounds), + normalization_nested_goals, + }), + }; + + debug_assert!(!value.has_infer(), "unexpected infer in {value:?}"); + debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); + let (max_universe, _variables, var_kinds) = canonicalizer.finalize(); + + Canonical { max_universe, var_kinds, value } + } + // The return value is the canonicalized `param_env`, plus a canonicalizer suitable for // canonicalizing the rest of the input. (For efficiency, and when appropriate, the returned // canonicalizer will be the same one used on `param_env`, with suitable modifications.) @@ -219,7 +301,36 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { predefined_opaques_in_body }; - let value = QueryInput { goal, predefined_opaques_in_body }; + // Filter out irrelevant hidden tys, in a fixed-point iteration. Otherwise it would make + // the query heavy and less cache-friendly. + let mut hidden_types_of_opaques_in_body_candidates = + input.hidden_types_of_opaques_in_body.to_vec(); + let mut hidden_types_of_opaques_in_body = vec![]; + while !hidden_types_of_opaques_in_body_candidates.is_empty() { + let prev_len = hidden_types_of_opaques_in_body.len(); + hidden_types_of_opaques_in_body_candidates.retain(|bound @ (hidden_ty, _)| { + if let ty::Infer(ty::TyVar(vid)) = hidden_ty.kind() + && rest_canonicalizer + .state + .sub_root_lookup_table + .contains_key(&delegate.sub_unification_table_root_var(vid)) + { + hidden_types_of_opaques_in_body.push(bound.fold_with(&mut rest_canonicalizer)); + false + } else { + true + } + }); + if hidden_types_of_opaques_in_body.len() == prev_len { + break; + } + } + + let hidden_types_of_opaques_in_body = + delegate.cx().mk_opaque_hidden_ty_bounds_in_body(&hidden_types_of_opaques_in_body); + + let value = + QueryInput { goal, predefined_opaques_in_body, hidden_types_of_opaques_in_body }; debug_assert!(!value.has_infer(), "unexpected infer in {value:?}"); debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 0d8620c3614a2..f84201a331957 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -27,7 +27,8 @@ use tracing::instrument; use crate::delegate::SolverDelegate; use crate::solve::{ CanonicalResponse, Certainty, ExternalConstraintsData, ExternalRegionConstraints, Goal, - NestedNormalizationGoals, QueryInput, Response, VisibleForLeakCheck, inspect, + NestedNormalizationGoals, QueryInput, RawExternalConstraintsData, Response, + VisibleForLeakCheck, inspect, }; pub mod canonicalizer; @@ -56,6 +57,7 @@ pub(super) fn canonicalize_goal( delegate: &D, goal: Goal, opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], + opaque_hidden_type_bounds: &[(I::Ty, ty::OpaqueHiddenTyBound)], typing_mode: TypingMode, ) -> (ThinVec, I::CanonicalInput) where @@ -67,6 +69,9 @@ where QueryInput { goal, predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types), + hidden_types_of_opaques_in_body: delegate + .cx() + .mk_opaque_hidden_ty_bounds_in_body(opaque_hidden_type_bounds), }, ); @@ -77,17 +82,24 @@ where (orig_values, query_input) } -pub(super) fn canonicalize_response( +pub(super) fn canonicalize_response( delegate: &D, max_input_universe: ty::UniverseIndex, - value: T, -) -> ty::Canonical + var_values: CanonicalVarValues, + certainty: Certainty, + external_constraints: RawExternalConstraintsData, +) -> ty::Canonical> where D: SolverDelegate, I: Interner, - T: TypeFoldable, { - Canonicalizer::canonicalize_response(delegate, max_input_universe, value) + Canonicalizer::canonicalize_query_response( + delegate, + max_input_universe, + var_values, + certainty, + external_constraints, + ) } /// After calling a canonical query, we apply the constraints returned @@ -117,8 +129,12 @@ where unify_query_var_values(delegate, param_env, &original_values, var_values, span); - let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } = - &*external_constraints; + let ExternalConstraintsData { + region_constraints, + opaque_types, + opaque_hidden_type_bounds, + normalization_nested_goals, + } = &*external_constraints; match region_constraints { ExternalRegionConstraints::Old(r) => register_region_constraints( @@ -138,7 +154,8 @@ where delegate.register_solver_region_constraint(r.clone(), span) } }; - register_new_opaque_types(delegate, opaque_types, span); + register_new_opaque_types(delegate, opaque_types.as_slice(), span); + delegate.add_opaque_hidden_ty_bounds_in_storage(opaque_hidden_type_bounds.as_slice()); (normalization_nested_goals.clone(), certainty) } 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 51e1aea3850e4..b16285958e7ff 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -14,9 +14,9 @@ use rustc_type_ir::solve::{ RerunNonErased, RerunReason, RerunResultExt, SizedTraitKind, StalledOnCoroutines, }; use rustc_type_ir::{ - self as ty, AliasTy, Interner, MayBeErased, Region, TypeFlags, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - TypingMode, Unnormalized, Upcast, elaborate, + self as ty, AliasTy, Interner, MayBeErased, Region, TypeFlags, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Unnormalized, + Upcast, elaborate, }; use tracing::{debug, instrument}; @@ -392,6 +392,25 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Result, NoSolutionOrRerunNonErased>; + + fn consider_hidden_types_of_opaques_bound_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + bound: ty::OpaqueHiddenTyBound, + ) -> Result, NoSolutionOrRerunNonErased> { + let assumption = bound.instantiate(ecx.cx(), goal.predicate.self_ty()); + Self::probe_and_match_goal_against_assumption( + ecx, + CandidateSource::AliasBound(AliasBoundKind::SelfBounds), + goal, + assumption, + |ecx| { + // We want to reprove this goal once we've inferred the + // hidden type, so we force the certainty to `Maybe`. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + }, + ) + } } /// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit @@ -1086,8 +1105,10 @@ where ) -> Result<(), RerunNonErased> { let self_ty = goal.predicate.self_ty(); // We only use this hack during HIR typeck. - let opaque_types = match self.typing_mode() { - TypingMode::Typeck { .. } => self.opaques_with_sub_unified_hidden_type(self_ty), + let hidden_types_of_opaques = match self.typing_mode() { + TypingMode::Typeck { .. } => { + self.hidden_types_of_opaques_modulo_sub_unification(self_ty) + } TypingMode::Coherence | TypingMode::PostTypeckUntilBorrowck { .. } | TypingMode::PostBorrowck { .. } @@ -1101,62 +1122,24 @@ where } }; - if opaque_types.is_empty() { + if hidden_types_of_opaques.is_empty() { candidates.extend(self.forced_ambiguity(MaybeInfo::AMBIGUOUS)); return Ok(()); } - for &opaque_ty in &opaque_types { - debug!("self ty is sub unified with {opaque_ty:?}"); - - struct ReplaceOpaque { - cx: I, - opaque_ty: ty::OpaqueAliasTy, - 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(is_rigid, alias_ty) = ty.kind() - && let Some(opaque_ty) = alias_ty.try_to_opaque() - { - if opaque_ty == self.opaque_ty { - debug_assert_eq!(is_rigid, ty::IsRigid::No); - return self.self_ty; - } - } - ty.super_fold_with(self) - } - } + for (hidden_ty, bounds) in hidden_types_of_opaques { + debug!("self ty is sub unified with {hidden_ty:?}"); - // We look at all item-bounds of the opaque, replacing the - // opaque with the current self type before considering - // them as a candidate. Imagine we've got `?x: Trait` - // and `?x` has been sub-unified with the hidden type of - // `impl Trait`, We take the item bound `opaque: Trait` + // We look at all item-bounds of the hidden types, replacing the + // instantiating the self type of the bound with the current self + // type before considering them as a candidate. Imagine we've got + // `?x: Trait` and `?x` has been sub-unified with the hidden + // type of `impl Trait`, We take the item bound `opaque: Trait` // and replace all occurrences of `opaque` with `?x`. This results // in a `?x: Trait` alias-bound candidate. - for item_bound in self - .cx() - .item_self_bounds(opaque_ty.kind.into()) - .iter_instantiated(self.cx(), opaque_ty.args) - .map(Unnormalized::skip_norm_wip) - { - let assumption = - item_bound.fold_with(&mut ReplaceOpaque { cx: self.cx(), opaque_ty, self_ty }); - candidates.extend(G::probe_and_match_goal_against_assumption( - self, - CandidateSource::AliasBound(AliasBoundKind::SelfBounds), - goal, - assumption, - |ecx| { - // We want to reprove this goal once we've inferred the - // hidden type, so we force the certainty to `Maybe`. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - }, - )); + for bound in bounds { + candidates + .extend(G::consider_hidden_types_of_opaques_bound_candidate(self, goal, bound)); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs index 988850bc64c5b..26941e3a255cd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs @@ -78,13 +78,14 @@ where GoalStalledOnOpaques::No => {} &GoalStalledOnOpaques::Yes { num_opaques_in_storage, + num_hidden_ty_bounds_in_storage, ref previously_succeeded_in_erased, } => { // If any opaques changed in the opaque type storage, // rerunning might make progress so we should rerun. if delegate .opaque_types_storage_num_entries() - .needs_reevaluation(num_opaques_in_storage) + .needs_reevaluation(num_opaques_in_storage, num_hidden_ty_bounds_in_storage) { // Unless this goal previously succeeded in erased mode. // If the stalled goal successfully evaluated while erasing opaque types, 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 697a7ad53464b..1a1950097f4b4 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 @@ -24,7 +24,7 @@ use rustc_type_ir::{ use thin_vec::ThinVec; use tracing::{Level, debug, instrument, trace, warn}; -use super::has_only_region_constraints; +use super::{RawExternalConstraintsData, has_only_region_constraints_or_opaque_hidden_ty_bounds}; use crate::canonical::{ canonicalize_goal, canonicalize_response, instantiate_and_apply_query_response, response_no_constraints_raw, @@ -41,10 +41,9 @@ use crate::solve::fast_path::compute_goal_fast_path_cold; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ - CanonicalResponse, Certainty, ExternalConstraintsData, FIXPOINT_STEP_LIMIT, Goal, - GoalEvaluation, GoalSource, GoalStalledOn, GoalStalledOnOpaques, HasChanged, MaybeCause, - NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, SucceededInErased, - VisibleForLeakCheck, inspect, + CanonicalResponse, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalSource, + GoalStalledOn, GoalStalledOnOpaques, HasChanged, MaybeCause, NestedNormalizationGoals, + NoSolution, QueryInput, QueryResult, SucceededInErased, VisibleForLeakCheck, inspect, }; pub mod fast_path; @@ -542,6 +541,10 @@ where } } + delegate.add_opaque_hidden_ty_bounds_in_storage( + input.hidden_types_of_opaques_in_body.as_slice(), + ); + let initial_opaque_types_storage_num_entries = delegate.opaque_types_storage_num_entries(); if cfg!(debug_assertions) && delegate.typing_mode_raw().is_erased_not_coherence() { assert!(delegate.clone_opaque_types_lookup_table().is_empty()); @@ -658,16 +661,19 @@ where // so we only canonicalize the lookup table and ignore // duplicate entries. let opaque_types = self.delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = eager_resolve_vars(&**self.delegate, (goal, opaque_types)); + let hidden_types_of_opaques = self.delegate.clone_opaque_hidden_ty_bounds(); + let (goal, opaque_types, hidden_types_of_opaques) = + eager_resolve_vars(&**self.delegate, (goal, opaque_types, hidden_types_of_opaques)); let typing_mode = self.typing_mode(); let step_kind = self.step_kind_for_source(source); let tracing_span = tracing::span!( Level::DEBUG, "evaluate_goal_raw in typing mode", - "{:?} opaques={:?}", + "{:?} opaques={:?}, hidden_types_of_opaques={:?}", typing_mode, - opaque_types + opaque_types, + hidden_types_of_opaques, ) .entered(); @@ -715,6 +721,7 @@ where self.delegate, goal, &[], + &[], TypingMode::ErasedNotCoherence(MayBeErased), ); @@ -755,8 +762,13 @@ where } } - let (orig_values, canonical_goal) = - canonicalize_goal(self.delegate, goal, &opaque_types, typing_mode); + let (orig_values, canonical_goal) = canonicalize_goal( + self.delegate, + goal, + &opaque_types, + &hidden_types_of_opaques, + typing_mode, + ); let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( self.cx(), @@ -787,8 +799,7 @@ where drop(tracing_span); - let has_changed = - if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; + let before_instantiate_response = self.delegate.num_opaque_hidden_type_bounds(); let (normalization_nested_goals, certainty) = instantiate_and_apply_query_response( self.delegate, @@ -798,6 +809,19 @@ where self.origin_span, ); + // `hidden_types_of_opaques` may vary modulo regions which might be able to be unified in + // the caller in the end. So, instead of the response has any, check whether the storage + // entries actually changed. + // + // FIXME: Add a test for this + let has_changed = if !has_only_region_constraints_or_opaque_hidden_ty_bounds(response) + || self.delegate.num_opaque_hidden_type_bounds() != before_instantiate_response + { + HasChanged::Yes + } else { + HasChanged::No + }; + // FIXME: We previously had an assert here that checked that recomputing // a goal after applying its constraints did not change its response. // @@ -865,16 +889,18 @@ where }) .collect(); + let num_opaques_in_storage = + canonical_goal.canonical.value.predefined_opaques_in_body.len(); + let num_hidden_ty_bounds_in_storage = + canonical_goal.canonical.value.hidden_types_of_opaques_in_body.len(); + GoalStalledOn { stalled_vars, sub_roots, stalled_maybe_info: maybe_info, opaques: GoalStalledOnOpaques::Yes { - num_opaques_in_storage: canonical_goal - .canonical - .value - .predefined_opaques_in_body - .len(), + num_opaques_in_storage, + num_hidden_ty_bounds_in_storage, previously_succeeded_in_erased, }, } @@ -1382,6 +1408,20 @@ where self.delegate.register_hidden_type_in_storage(opaque_type_key, hidden_ty, self.origin_span) } + pub(super) fn add_hidden_type_of_opaque_in_storage( + &self, + hidden_ty: I::Ty, + bounds: impl IntoIterator>, + ) { + self.delegate.add_hidden_type_of_opaque_in_storage(hidden_ty, bounds); + } + pub(super) fn add_opaque_hidden_type_bounds_in_storage( + &self, + bounds: &[(I::Ty, ty::OpaqueHiddenTyBound)], + ) { + self.delegate.add_opaque_hidden_ty_bounds_in_storage(bounds); + } + pub(super) fn add_item_bounds_for_hidden_type( &mut self, opaque_def_id: I::OpaqueTyId, @@ -1494,12 +1534,12 @@ where Ok(may_use_unstable_feature(&**self.delegate, param_env, symbol)) } - pub(crate) fn opaques_with_sub_unified_hidden_type( + pub(crate) fn hidden_types_of_opaques_modulo_sub_unification( &self, self_ty: I::Ty, - ) -> Vec> { + ) -> Vec<(I::Ty, Vec>)> { if let ty::Infer(ty::TyVar(vid)) = self_ty.kind() { - self.delegate.opaques_with_sub_unified_hidden_type(vid) + self.delegate.hidden_types_of_opaques_modulo_sub_unification(vid) } else { vec![] } @@ -1681,14 +1721,16 @@ where }); } + external_constraints + .opaque_hidden_type_bounds + .retain(|(hidden_ty, _)| hidden_ty.is_ty_var()); + let canonical = canonicalize_response( self.delegate, self.max_input_universe, - Response { - var_values, - certainty, - external_constraints: self.cx().mk_external_constraints(external_constraints), - }, + var_values, + certainty, + external_constraints, ); Ok(canonical) @@ -1722,7 +1764,7 @@ where &self, certainty: Certainty, normalization_nested_goals: NestedNormalizationGoals, - ) -> ExternalConstraintsData { + ) -> RawExternalConstraintsData { // We only return region constraints once the certainty is `Yes`. This // is necessary as we may drop nested goals on ambiguity, which may result // in unconstrained inference variables in the region constraints. It also @@ -1754,15 +1796,21 @@ where // // Constraints for any existing opaque types are already tracked by changes // to the `var_values`. - let opaque_types = self - .delegate - .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); + let initial_entries = self.initial_opaque_types_storage_num_entries; + let opaque_types = self.delegate.clone_opaque_types_added_since(initial_entries); + let opaque_hidden_type_bounds = + self.delegate.clone_opaque_hidden_ty_bounds_added_since(initial_entries); if self.typing_mode().is_erased_not_coherence() { - assert!(opaque_types.is_empty()); + assert!(opaque_types.is_empty() && opaque_hidden_type_bounds.is_empty()); } - ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } + RawExternalConstraintsData { + region_constraints, + opaque_types, + opaque_hidden_type_bounds, + normalization_nested_goals, + } } pub(super) fn normalize>( @@ -1928,11 +1976,18 @@ pub(super) fn evaluate_root_goal_for_proof_tree, root_depth: usize, ) -> (Result, NoSolution>, inspect::GoalEvaluation) { let opaque_types = delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = eager_resolve_vars(&**delegate, (goal, opaque_types)); + let hidden_types_of_opaques = delegate.clone_opaque_hidden_ty_bounds(); + let (goal, opaque_types, hidden_types_of_opaques) = + eager_resolve_vars(&**delegate, (goal, opaque_types, hidden_types_of_opaques)); let typing_mode = delegate.typing_mode_raw().assert_not_erased(); - let (orig_values, canonical_goal) = - canonicalize_goal(delegate, goal, &opaque_types, typing_mode.into()); + let (orig_values, canonical_goal) = canonicalize_goal( + delegate, + goal, + &opaque_types, + &hidden_types_of_opaques, + typing_mode.into(), + ); let (canonical_result, final_revision, required_depth) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal, root_depth); diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 8d20bcf4c7a6d..838c5aed4930c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -24,6 +24,7 @@ use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; use rustc_type_ir::{self as ty, Interner, Region, TypeVisitableExt}; +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -60,11 +61,13 @@ fn has_no_inference_or_external_constraints( let ExternalConstraintsData { ref region_constraints, ref opaque_types, + ref opaque_hidden_type_bounds, ref normalization_nested_goals, } = *response.value.external_constraints; response.value.var_values.is_identity() && region_constraints.is_empty() && opaque_types.is_empty() + && opaque_hidden_type_bounds.is_empty() && normalization_nested_goals.is_empty() } @@ -72,6 +75,22 @@ fn has_only_region_constraints(response: ty::Canonical( + response: ty::Canonical>, +) -> bool { + let ExternalConstraintsData { + region_constraints: _, + ref opaque_types, + opaque_hidden_type_bounds: _, ref normalization_nested_goals, } = *response.value.external_constraints; response.value.var_values.is_identity_modulo_regions() @@ -437,3 +456,12 @@ pub struct GoalEvaluation { /// before rerunning it. pub stalled_on: Option>, } + +#[derive_where(Clone, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +pub struct RawExternalConstraintsData { + pub region_constraints: ExternalRegionConstraints, + pub opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, + pub opaque_hidden_type_bounds: Vec<(I::Ty, ty::OpaqueHiddenTyBound)>, + pub normalization_nested_goals: NestedNormalizationGoals, +} diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index cb878f2c54878..c135994f39c24 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -4,8 +4,8 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::{ - FetchEligibleAssocItemResponse, NoSolutionOrRerunNonErased, QueryResultOrRerunNonErased, - RerunNonErased, RerunReason, RerunResultExt, + AliasBoundKind, FetchEligibleAssocItemResponse, NoSolutionOrRerunNonErased, + QueryResultOrRerunNonErased, RerunNonErased, RerunReason, RerunResultExt, }; use rustc_type_ir::{ self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Region, Unnormalized, Upcast as _, @@ -1086,6 +1086,48 @@ where ) -> Result, NoSolutionOrRerunNonErased> { unreachable!("try_as_dyn helper trait doesn't have assoc types") } + + fn consider_hidden_types_of_opaques_bound_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + bound: ty::OpaqueHiddenTyBound, + ) -> Result, NoSolutionOrRerunNonErased> { + let NormalizesTo { alias, term } = goal.predicate; + let cx = ecx.cx(); + let assumption = bound.instantiate(cx, alias.self_ty()); + Self::probe_and_match_goal_against_assumption( + ecx, + CandidateSource::AliasBound(AliasBoundKind::SelfBounds), + goal, + assumption, + |ecx| { + // Add self bounds for `::Assoc`. Those bounds should be added to + // storage if and only if `` is proven by + // `ty::OpaqueHiddenTyBound` of an opaque (or another assoc ty on it), otherwise + // it might make blaket impl candidate inapplicable. + // See `tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs` for such case. + if ecx.typing_mode().should_add_hidden_types_of_opaques() + && let ty::AliasTermKind::ProjectionTy { def_id } = alias.kind + { + ecx.add_hidden_type_of_opaque_in_storage( + term.expect_ty(), + ty::OpaqueHiddenTyBound::iter_item_self_bounds_for_hidden_ty( + cx, + ty::AliasTy::new_from_args( + cx, + ty::AliasTyKind::Projection { def_id }, + alias.args, + ), + ), + ); + } + + // We want to reprove this goal once we've inferred the + // hidden type, so we force the certainty to `Maybe`. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + }, + ) + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index db326e6d736a4..40b9b5e59726d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -3,6 +3,7 @@ mod free_alias; mod inherent; mod opaque_types; +use rustc_type_ir::inherent::*; use rustc_type_ir::search_graph::LowerAvailableDepth; use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, ProjectionClause}; @@ -51,6 +52,25 @@ where let normalizes_to = goal.with(self.cx(), ty::NormalizesTo { alias, term: unconstrained_term }); + // FIXME: Explain this hack. Why this is needed and why should be done here + // FIXME: Maybe we need probing for whole this call as the following lines + // directly add `hidden_types_of_opaques` to the context without probing or + // instantiating the response. + if self.typing_mode().should_add_hidden_types_of_opaques() + && unconstrained_term.as_type().is_some() + && alias.self_ty().is_ty_var() + { + let hidden_bounds = + self.hidden_types_of_opaques_modulo_sub_unification(alias.self_ty()); + if let Some(unmentioned) = ty::OpaqueHiddenTyBound::opt_unmentioned_projection_bound( + self.cx(), + hidden_bounds.iter().flat_map(|(_, bounds)| bounds).copied(), + goal.predicate, + ) { + self.add_opaque_hidden_type_bounds_in_storage(&[(alias.self_ty(), unmentioned)]); + } + } + // We don't want candidate selection when normalizing associated terms to be impacted by // the expected term. Normalization should behave like a function of just the alias being // normalized. Because of this, we use an internal `NormalizesTo` goal for which the diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs index 2f56056449779..68653e9bb83bd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs @@ -117,6 +117,19 @@ where goal.param_env, expected, )?; + if self.typing_mode().should_add_hidden_types_of_opaques() { + self.add_hidden_type_of_opaque_in_storage( + expected, + ty::OpaqueHiddenTyBound::iter_item_self_bounds_for_hidden_ty( + cx, + ty::AliasTy::new_from_args( + cx, + ty::AliasTyKind::Opaque { def_id: def_id.into() }, + normalized_args, + ), + ), + ); + } self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into) } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 818d8e1a4e0c3..23cba0c7a4cdf 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -89,6 +89,7 @@ fn goal_stalled_on_args_or_nonempty_opaques<'tcx>( stalled_maybe_info: MaybeInfo::AMBIGUOUS, opaques: GoalStalledOnOpaques::Yes { num_opaques_in_storage: 0, + num_hidden_ty_bounds_in_storage: 0, // This function should only be called when not in erased mode, // otherwise this is wrong. The `compute_goal_fast_path` does this // through `known_no_opaque_types_in_storage` diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index f7eb76bbe72b5..51f84500a0fe9 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -460,6 +460,6 @@ mod size_asserts { // `memcpy` for moving elements of `PendingObligations`. Then #160479 greatly reduced the // number of `memcpy` operations in `try_evaluate_obligations`. So the size of this pair is // much less important than it was, but still shouldn't be changed without some thought. - static_assert_size!((PredicateObligation<'_>, Option>>), 104); + static_assert_size!((PredicateObligation<'_>, Option>>), 112); // tidy-alphabetical-end } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 31fe3cf99d88c..a6f442f6acc7d 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -274,6 +274,23 @@ impl TypingMode { | TypingMode::Codegen => false, } } + + /// This is `true` for `Typeck` and `false` otherwise as we take into account the + /// item self bounds for `hidden_types_of_opaques` for that `TypingMode` only. + /// + /// See also the documentation on [`TypingMode`] about exhaustive matching. + pub fn should_add_hidden_types_of_opaques(&self) -> bool { + match self { + TypingMode::Typeck { .. } => true, + TypingMode::PostTypeckUntilBorrowck { .. } + | TypingMode::Coherence + | TypingMode::PostBorrowck { .. } + | TypingMode::Reflection + | TypingMode::PostAnalysis + | TypingMode::Codegen + | TypingMode::ErasedNotCoherence(_) => false, + } + } } impl TypingMode { @@ -553,20 +570,25 @@ pub trait InferCtxtLike: Sized { type OpaqueTypeStorageEntries: OpaqueTypeStorageEntries; fn opaque_types_storage_num_entries(&self) -> Self::OpaqueTypeStorageEntries; + fn num_opaque_hidden_type_bounds(&self) -> usize; fn clone_opaque_types_lookup_table( &self, ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; - fn clone_duplicate_opaque_types( + fn clone_opaque_hidden_ty_bounds( &self, - ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + ) -> Vec<(::Ty, ty::OpaqueHiddenTyBound)>; fn clone_opaque_types_added_since( &self, prev_entries: Self::OpaqueTypeStorageEntries, ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; - fn opaques_with_sub_unified_hidden_type( + fn clone_opaque_hidden_ty_bounds_added_since( + &self, + prev_entries: Self::OpaqueTypeStorageEntries, + ) -> Vec<(::Ty, ty::OpaqueHiddenTyBound)>; + fn hidden_types_of_opaques_modulo_sub_unification( &self, - ty: TyVid, - ) -> Vec>; + ty_vid: TyVid, + ) -> Vec<(::Ty, Vec>)>; fn register_hidden_type_in_storage( &self, @@ -580,6 +602,15 @@ pub trait InferCtxtLike: Sized { hidden_ty: ::Ty, span: ::Span, ); + fn add_hidden_type_of_opaque_in_storage( + &self, + hidden_ty: ::Ty, + bounds: impl IntoIterator>, + ); + fn add_opaque_hidden_ty_bounds_in_storage( + &self, + bounds: &[(::Ty, ty::OpaqueHiddenTyBound)], + ); fn reset_opaque_types(&self); } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index bf90ef707c051..1e6111b455b5f 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -676,11 +676,11 @@ pub trait Span: Copy + Debug + Hash + Eq + TypeFoldable { } #[rust_analyzer::prefer_underscore_import] -pub trait OpaqueTypeStorageEntries: Debug + Copy + Default { +pub trait OpaqueTypeStorageEntries: Debug + Clone + Copy + Default + PartialEq + Eq { /// Whether the number of opaques has changed in a way that necessitates /// reevaluating a goal. For now, this is only when the number of non-duplicated - /// entries changed. - fn needs_reevaluation(self, canonicalized: usize) -> bool; + /// entries and bounds for hidden types of opaques changed. + fn needs_reevaluation(self, opaques: usize, hidden_ty_bounds: usize) -> bool; } pub trait BoundVarKinds: diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 31a027c15fd01..53daa4e1b461b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -129,6 +129,17 @@ pub trait Interner: data: &[(ty::OpaqueTypeKey, Self::Ty)], ) -> Self::PredefinedOpaques; + type OpaqueHiddenTyBounds: Copy + + Debug + + Hash + + Eq + + TypeFoldable + + SliceLike)>; + fn mk_opaque_hidden_ty_bounds_in_body( + self, + data: &[(Self::Ty, ty::OpaqueHiddenTyBound)], + ) -> Self::OpaqueHiddenTyBounds; + type LocalDefIds: Copy + Debug + Hash diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index 51b3860ba1ff1..a2d6a09ede5e8 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -1,10 +1,14 @@ use derive_where::derive_where; +use rustc_index::Idx; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash_NoContext}; use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; -use crate::{self as ty, Interner, Region}; +use crate::{ + self as ty, Binder, Interner, Region, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, Upcast, +}; #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] @@ -48,3 +52,208 @@ impl OpaqueTypeKey { Self { def_id, args } } } + +/// An item self bound for a hidden type(either an opaque or projection onto another hidden type). +/// This is meant to be instantiated inside the solver into an assumption for a goal with the goal's +/// self ty to support non-defining usages. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) +)] +pub struct OpaqueHiddenTyBound { + bound: Binder, +} + +impl Eq for OpaqueHiddenTyBound {} + +impl OpaqueHiddenTyBound { + /// Iterate through the item self bounds of a hidden type for either an opaque + /// or a projection onto another hidden ty. + pub fn iter_item_self_bounds_for_hidden_ty( + cx: I, + alias: ty::AliasTy, + ) -> impl Iterator { + let def_id = match alias.kind { + ty::AliasTyKind::Projection { def_id } => def_id.into(), + ty::AliasTyKind::Opaque { def_id } => def_id.into(), + ty::AliasTyKind::Inherent { .. } | ty::AliasTyKind::Free { .. } => unreachable!( + "Opaque hidden type should be either an opaque type or projection on another hidden type" + ), + }; + + let args = alias.args; + let alias = I::Ty::new_alias(cx, ty::IsRigid::No, alias); + cx.item_self_bounds(def_id).iter_instantiated(cx, args).map(move |bound| { + let bound = Binder::bind_with_vars( + bound + .skip_normalization() + .fold_with(&mut ReplaceSelfTyWithAnonBound::new(cx, alias)), + I::BoundVarKinds::from_vars(cx, [ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon)]), + ); + OpaqueHiddenTyBound { bound } + }) + } + + /// If the given `projection` is not mentioned among the given `existing_bounds`, + /// create one for it. + /// + /// This is needed to support the non-defining usages like in the following case: + /// + /// ```no_run + /// fn argument_types() -> impl IntoIterator { + /// argument_types().into_iter().collect::>() + /// // ^ ^ + /// // | | + /// // `{opaque}` | + /// // `<{opaque} as IntoIterator>::IntoIter` + /// } + /// ``` + /// + /// We need to prove `<{opaque} as IntoIterator>::IntoIter: Iterator` to select the + /// method `collect()` on it. But as the given bounds in the scope don't mention the + /// assoc type `IntoIterator::IntoIter` at all, we can't assemble a candidate for + /// that trait goal. So, we have manually conjure a bound for such unmentioned + /// projections. + pub fn opt_unmentioned_projection_bound( + cx: I, + existing_bounds: impl IntoIterator, + proj: ty::ProjectionClause, + ) -> Option { + let trait_def_id = proj.trait_def_id(cx); + let mut mentions_trait = false; + for bound in existing_bounds.into_iter() { + if bound + .bound + .skip_binder() + .as_projection_clause() + .is_some_and(|b| b.item_def_id() == proj.def_id()) + { + // Mentioned already + return None; + } + + if bound + .bound + .skip_binder() + .as_trait_clause() + .is_some_and(|b| b.def_id() == trait_def_id) + { + mentions_trait = true; + } + } + + if !mentions_trait { + return None; + } + + let bound: I::Clause = proj.upcast(cx); + let bound = Binder::bind_with_vars( + bound.fold_with(&mut ReplaceSelfTyWithAnonBound::new(cx, proj.self_ty())), + I::BoundVarKinds::from_vars(cx, [ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon)]), + ); + Some(OpaqueHiddenTyBound { bound }) + } + + pub fn instantiate(self, cx: I, self_ty: I::Ty) -> I::Clause { + let OpaqueHiddenTyBound { bound } = self; + + debug_assert_eq!( + bound.bound_vars().as_slice(), + &[ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon)] + ); + debug_assert!(bound.skip_binder().has_escaping_bound_vars()); + + let bound = + self.bound.skip_binder().fold_with(&mut ReplaceAnonBoundWithSelfTy::new(cx, self_ty)); + debug_assert!(!bound.has_escaping_bound_vars()); + + bound + } +} + +struct ReplaceSelfTyWithAnonBound { + cx: I, + self_ty: I::Ty, + debruijn: ty::DebruijnIndex, + bound_var: ty::BoundVar, +} + +impl ReplaceSelfTyWithAnonBound { + fn new(cx: I, self_ty: I::Ty) -> Self { + ReplaceSelfTyWithAnonBound { + cx, + self_ty, + debruijn: ty::INNERMOST, + bound_var: ty::BoundVar::new(0), + } + } +} + +impl TypeFolder for ReplaceSelfTyWithAnonBound { + fn cx(&self) -> I { + self.cx + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if ty == self.self_ty { + I::Ty::new_anon_bound(self.cx, self.debruijn, self.bound_var) + } else { + ty.super_fold_with(self) + } + } + + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder + where + T: TypeFoldable, + { + self.debruijn.shift_in(1); + let result = t.super_fold_with(self); + self.debruijn.shift_out(1); + result + } +} + +struct ReplaceAnonBoundWithSelfTy { + cx: I, + self_ty: I::Ty, + debruijn: ty::DebruijnIndex, + bound_ty: ty::BoundTy, +} + +impl ReplaceAnonBoundWithSelfTy { + fn new(cx: I, self_ty: I::Ty) -> Self { + ReplaceAnonBoundWithSelfTy { + cx, + self_ty, + debruijn: ty::INNERMOST, + bound_ty: ty::BoundTy { var: ty::BoundVar::new(0), kind: ty::BoundTyKind::Anon }, + } + } +} + +impl TypeFolder for ReplaceAnonBoundWithSelfTy { + fn cx(&self) -> I { + self.cx + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + let ty = ty.super_fold_with(self); + if ty::Bound(ty::BoundVarIndexKind::Bound(self.debruijn), self.bound_ty) == ty.kind() { + self.self_ty + } else { + ty + } + } + + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder + where + T: TypeFoldable, + { + self.debruijn.shift_in(1); + let result = t.super_fold_with(self); + self.debruijn.shift_out(1); + result + } +} diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index d1a24e0054115..d7b14d1573fb2 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -456,6 +456,7 @@ pub enum GoalSource { pub struct QueryInput { pub goal: Goal, pub predefined_opaques_in_body: I::PredefinedOpaques, + pub hidden_types_of_opaques_in_body: I::OpaqueHiddenTyBounds, } impl Eq for QueryInput {} @@ -563,7 +564,7 @@ pub enum ParamEnvSource { pub enum AliasBoundKind { /// Alias bound from the self type of a projection SelfBounds, - // Alias bound having recursed on the self type of a projection + /// Alias bound having recursed on the self type of a projection NonSelfBounds, } @@ -630,7 +631,8 @@ impl ExternalRegionConstraints { #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub struct ExternalConstraintsData { pub region_constraints: ExternalRegionConstraints, - pub opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, + pub opaque_types: I::PredefinedOpaques, + pub opaque_hidden_type_bounds: I::OpaqueHiddenTyBounds, pub normalization_nested_goals: NestedNormalizationGoals, } @@ -645,7 +647,8 @@ impl ExternalConstraintsData { Self { region_constraints, - opaque_types: vec![], + opaque_types: cx.mk_predefined_opaques_in_body(&[]), + opaque_hidden_type_bounds: cx.mk_opaque_hidden_ty_bounds_in_body(&[]), normalization_nested_goals: NestedNormalizationGoals::default(), } } @@ -654,10 +657,12 @@ impl ExternalConstraintsData { let ExternalConstraintsData { region_constraints, opaque_types, + opaque_hidden_type_bounds, normalization_nested_goals, } = self; region_constraints.is_empty() && opaque_types.is_empty() + && opaque_hidden_type_bounds.is_empty() && normalization_nested_goals.is_empty() } } @@ -1000,6 +1005,7 @@ pub enum GoalStalledOnOpaques { No, Yes { num_opaques_in_storage: usize, + num_hidden_ty_bounds_in_storage: usize, previously_succeeded_in_erased: SucceededInErased, }, } diff --git a/tests/ui/traits/next-solver/opaques/non-defining-use-projection-on-hidden-1.rs b/tests/ui/traits/next-solver/opaques/non-defining-use-projection-on-hidden-1.rs new file mode 100644 index 0000000000000..18208b18f2abd --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/non-defining-use-projection-on-hidden-1.rs @@ -0,0 +1,27 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for + +#![allow(warnings)] + +fn foo() -> impl IntoIterator { + if false { + let x: Vec<_> = foo().into_iter().collect(); + } + + [1, 2] +} + +// `Flatten: Iterator` is ambiguous. +fn move_forward() -> impl IntoIterator { + std::iter::empty().map(|_: ()| move_forward()).flatten().collect::>() +} + +fn argument_types() -> impl IntoIterator { + argument_types().into_iter().collect::>() +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/non-defining-use-projection-on-hidden-2.rs b/tests/ui/traits/next-solver/opaques/non-defining-use-projection-on-hidden-2.rs new file mode 100644 index 0000000000000..d3b87eabd837b --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/non-defining-use-projection-on-hidden-2.rs @@ -0,0 +1,46 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for +// Test whether we can call methods on not-yet defined nested projections on opaques such as +// `<<{opaque}> as Baz>::Assoc as Bar>::Assoc`. + +#![allow(warnings)] + +trait Foo { + fn foo(&self) {} +} + +trait Bar { + type Assoc: Foo; + + fn bar(&self) -> Self::Assoc { + loop {} + } +} + +trait Baz { + type Assoc: Bar; + + fn baz(&self) -> Self::Assoc { + loop {} + } +} + +impl Foo for () {} + +impl Bar for () { + type Assoc = (); +} + +impl Baz for () { + type Assoc = (); +} + +fn heck() -> impl Baz { + heck().baz().bar().foo() +} + +fn main() {}