Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down Expand Up @@ -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)?;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
61 changes: 42 additions & 19 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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)

@adwinwhite adwinwhite Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be obvious but I'm struggling to understand why we can detect the projection term via hidden infer var. 😢
Issue 248 is about <hidden as Trait>::Projection: ProjectionItemBound. The self_ty in method lookup is an fresh infer var from normalizing <hidden as Trait>::Projection, I presume? So it's not the hidden infer var. However, item bounds we gather from the projection item are keyed by hidden 🤔

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The self_ty in method lookup is an fresh infer var from normalizing <hidden as Trait>::Projection, I presume?

Yes, it is. So has_hidden_types_of_opaques_modulo_sub_unification checks both cases for ?fresh_infer_var_for_opaque and ?fresh_infer_var_for_possibly_multiple_projection_on_opaque), the key for those bounds.

It is necessary to check the later as we have the cases with <<{opaque} as TraitA>::Assoc> as TraitB>::Assoc like the following, which is compiled with the old solver (and this PR) but not with the next-solver:

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() {}

@adwinwhite adwinwhite Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So has_hidden_types_of_opaques_modulo_sub_unification checks both cases for ?fresh_infer_var_for_opaque and ?fresh_infer_var_for_possibly_multiple_projection_on_opaque), the key for those bounds.

In the method body, has_hidden_types_of_opaques_modulo_sub_unification only checks the keys of hidden_types_of_opaques so you mean those keys also contains fresh infer vars from normalizing projections 🤔
But I don't find where we add bounds for these fresh infer vars? They're all for hidden infers of opaques?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally got it. Thank you for the explanation!
We could use better naming for these things now that they handle more than hidden types of opaques?
Ofc we should fix correctness issues first :>

@ShoyuVanilla ShoyuVanilla Aug 27, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I really should fix those namings 😄 I hope most of the correctness things(except folding binders and some canonicalization things) might be fixed by now so I'll try the perf and the naming/comments sides soon

} else {
false
}
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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]
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ impl<'tcx> InferCtxt<'tcx> {
.borrow_mut()
.opaque_type_storage
.take_opaque_types()
.0
.map(|(k, v)| (k, v.ty))
.collect();

Expand Down
40 changes: 32 additions & 8 deletions compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ty::OpaqueAliasTy<'tcx>> {
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<ty::OpaqueHiddenTyBound<'tcx>>)> {
self.hidden_types_of_opaques_modulo_sub_unification(ty_vid)
}

fn register_hidden_type_in_storage(
Expand All @@ -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<Item = ty::OpaqueHiddenTyBound<'tcx>>,
) {
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();
Expand Down
49 changes: 43 additions & 6 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -1121,25 +1121,32 @@ 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<ty::OpaqueHiddenTyBound<'tcx>>)>,
) {
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)]
pub fn clone_opaque_types(&self) -> Vec<(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'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;
}

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;
Expand Down Expand Up @@ -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<ty::OpaqueHiddenTyBound<'tcx>>)> {
// 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<DefId>) -> bool {
debug_assert!(!self.next_trait_solver());
Expand Down
Loading
Loading