Skip to content
Draft
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub(crate) fn clone_and_resolve_opaque_types<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> (OpaqueTypeStorageEntries, Vec<(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)>) {
) -> (OpaqueTypeStorageEntries<'tcx>, Vec<(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)>) {
let opaque_types = infcx.clone_opaque_types();
let opaque_types_storage_num_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
let opaque_types = opaque_types
Expand Down Expand Up @@ -665,7 +665,7 @@ pub(crate) fn handle_unconstrained_hidden_type_errors<'tcx>(
/// See the related comment in `FnCtxt::detect_opaque_types_added_during_writeback`.
pub(crate) fn detect_opaque_types_added_while_handling_opaque_types<'tcx>(
infcx: &InferCtxt<'tcx>,
opaque_types_storage_num_entries: OpaqueTypeStorageEntries,
opaque_types_storage_num_entries: &OpaqueTypeStorageEntries<'tcx>,
) {
for (key, hidden_type) in infcx
.inner
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/root_cx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> {

detect_opaque_types_added_while_handling_opaque_types(
&input.infcx,
opaque_types_storage_num_entries,
&opaque_types_storage_num_entries,
)
}
}
Expand Down
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
75 changes: 50 additions & 25 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::cell::{Cell, RefCell};
use std::cmp::max;
use std::debug_assert_matches;
use std::ops::Deref;
use std::{debug_assert_matches, iter};

use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sso::SsoHashSet;
Expand Down Expand Up @@ -419,7 +419,20 @@ 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_ty_bounds().flat_map(
|(hidden_ty, bounds)| iter::repeat(hidden_ty).zip(bounds.iter().copied()),
),
)
} 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,17 +447,21 @@ 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 {
steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
self_ty: self.make_query_response_ignoring_pending_obligations(
var_values,
self_ty,
prev_opaque_entries,
&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 +649,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 +674,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 @@ -699,9 +722,9 @@ pub(crate) fn method_autoderef_steps<'tcx>(
self_ty: infcx.make_query_response_ignoring_pending_obligations(
inference_vars,
ty,
prev_opaque_entries,
&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 @@ -723,9 +746,9 @@ pub(crate) fn method_autoderef_steps<'tcx>(
self_ty: infcx.make_query_response_ignoring_pending_obligations(
inference_vars,
ty,
prev_opaque_entries,
&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,20 +765,22 @@ 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(
inference_vars,
final_ty,
prev_opaque_entries,
&prev_opaque_entries,
),
}),
ty::Array(elem_ty, _) => {
Expand All @@ -764,9 +789,9 @@ pub(crate) fn method_autoderef_steps<'tcx>(
self_ty: infcx.make_query_response_ignoring_pending_obligations(
inference_vars,
Ty::new_slice(infcx.tcx, *elem_ty),
prev_opaque_entries,
&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 +2318,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
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
pub(super) fn detect_opaque_types_added_during_writeback(&self) {
let num_entries = self.checked_opaque_types_storage_entries.take().unwrap();
for (key, hidden_type) in
self.inner.borrow_mut().opaque_types().opaque_types_added_since(num_entries)
self.inner.borrow_mut().opaque_types().opaque_types_added_since(&num_entries)
{
let opaque_type_string = self.tcx.def_path_str(key.def_id);
let msg = format!("unexpected cyclic definition of `{opaque_type_string}`");
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
// Used to detect opaque types uses added after we've already checked them.
//
// See [FnCtxt::detect_opaque_types_added_during_writeback] for more details.
pub(super) checked_opaque_types_storage_entries: Cell<Option<OpaqueTypeStorageEntries>>,
pub(super) checked_opaque_types_storage_entries: Cell<Option<OpaqueTypeStorageEntries<'tcx>>>,

/// Some additional `Sized` obligations badly affect type inference.
/// These obligations are added in a later stage of typeck.
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<'tcx> InferCtxt<'tcx> {
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
prev_entries: OpaqueTypeStorageEntries,
prev_entries: &OpaqueTypeStorageEntries<'tcx>,
) -> Canonical<'tcx, QueryResponse<'tcx, T>>
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
Expand Down 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
50 changes: 42 additions & 8 deletions compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! Definition of `InferCtxtLike` from the librarified type layer.
use std::iter;

use rustc_data_structures::sso::SsoHashMap;
use rustc_hir::def_id::DefId;
use rustc_middle::traits::ObligationCause;
Expand Down Expand Up @@ -335,25 +337,31 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.register_type_outlives_constraint(ty, r, &ObligationCause::dummy_with_span(span));
}

type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries;
type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries<'tcx>;
#[inline]
fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries {
fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries<'tcx> {
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 needs_reevaluation(&self, opaques: usize, hidden_ty_bounds: usize) -> bool {
self.inner.borrow_mut().opaque_types().needs_reevaluation(opaques, hidden_ty_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_duplicate_entries()
.map(|(k, h)| (k, h.ty))
.iter_opaque_hidden_ty_bounds()
.flat_map(|(hidden_ty, bounds)| iter::repeat(hidden_ty).zip(bounds.iter().copied()))
.collect()
}
fn clone_opaque_types_added_since(
&self,
prev_entries: OpaqueTypeStorageEntries,
prev_entries: &OpaqueTypeStorageEntries<'tcx>,
) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
self.inner
.borrow_mut()
Expand All @@ -362,8 +370,21 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
.map(|(k, h)| (k, h.ty))
.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 clone_opaque_hidden_ty_bounds_added_since(
&self,
prev_entries: &OpaqueTypeStorageEntries<'tcx>,
) -> Vec<(Ty<'tcx>, ty::OpaqueHiddenTyBound<'tcx>)> {
self.inner
.borrow_mut()
.opaque_types()
.opaque_hidden_ty_bounds_added_since(prev_entries)
.collect()
}
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 +409,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
Loading
Loading