Skip to content
Open
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
35 changes: 18 additions & 17 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(

for trait_projection in collector.types.into_iter().rev() {
let impl_opaque_args = trait_projection.args.rebase_onto(tcx, trait_m.def_id, impl_m_args);
let hidden_ty = hidden_tys[&trait_projection.kind.def_id()]
.instantiate(tcx, impl_opaque_args)
.skip_norm_wip();
let hidden_ty =
hidden_tys[&trait_projection.kind].instantiate(tcx, impl_opaque_args).skip_norm_wip();

// If the hidden type is not an opaque, then we have "refined" the trait signature.
let ty::Alias(
impl_opaque @ ty::AliasTy { kind: ty::Opaque { def_id: impl_opaque_def_id }, .. },
) = *hidden_ty.kind()
else {
let impl_opaque = if let ty::Alias(alias) = *hidden_ty.kind()
&& let Some(impl_opaque) = alias.try_to_opaque()
{
impl_opaque
} else {
report_mismatched_rpitit_signature(
tcx,
trait_m_sig_with_self_for_diag,
Expand All @@ -105,7 +105,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(

// This opaque also needs to be from the impl method -- otherwise,
// it's a refinement to a TAIT.
if !tcx.hir_get_if_local(impl_opaque_def_id).is_some_and(|node| {
if !tcx.hir_get_if_local(impl_opaque.kind).is_some_and(|node| {
matches!(
node.expect_opaque_ty().origin,
hir::OpaqueTyOrigin::AsyncFn { parent, .. } | hir::OpaqueTyOrigin::FnReturn { parent, .. }
Expand All @@ -124,13 +124,13 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
}

trait_bounds.extend(
tcx.item_bounds(trait_projection.kind.def_id())
tcx.item_bounds(trait_projection.kind)
.iter_instantiated(tcx, trait_projection.args)
.map(Unnormalized::skip_norm_wip),
);
impl_bounds.extend(elaborate(
tcx,
tcx.explicit_item_bounds(impl_opaque_def_id)
tcx.explicit_item_bounds(impl_opaque.kind)
.iter_instantiated_copied(tcx, impl_opaque.args)
.map(Unnormalized::skip_norm_wip),
));
Expand Down Expand Up @@ -231,7 +231,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
// is literally unrepresentable in the type system; however, we may be
// promising stronger outlives guarantees if we capture *fewer* regions.
for (trait_projection, impl_opaque) in pairs {
let impl_variances = tcx.variances_of(impl_opaque.kind.def_id());
let impl_variances = tcx.variances_of(impl_opaque.kind);
let impl_captures: FxIndexSet<_> = impl_opaque
.args
.iter()
Expand All @@ -240,7 +240,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
.map(|(arg, _)| arg)
.collect();

let trait_variances = tcx.variances_of(trait_projection.kind.def_id());
let trait_variances = tcx.variances_of(trait_projection.kind);
let mut trait_captures = FxIndexSet::default();
for (arg, variance) in trait_projection.args.iter().zip_eq(trait_variances) {
if *variance != ty::Invariant {
Expand All @@ -252,7 +252,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
if !trait_captures.iter().all(|arg| impl_captures.contains(arg)) {
report_mismatched_rpitit_captures(
tcx,
impl_opaque.kind.def_id().expect_local(),
impl_opaque.kind.expect_local(),
trait_captures,
is_internal,
);
Expand All @@ -262,18 +262,19 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(

struct ImplTraitInTraitCollector<'tcx> {
tcx: TyCtxt<'tcx>,
types: FxIndexSet<ty::AliasTy<'tcx>>,
types: FxIndexSet<ty::ProjectionAliasTy<'tcx>>,
}

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) {
if let ty::Alias(proj @ ty::AliasTy { kind: ty::Projection { def_id }, .. }) = *ty.kind()
&& self.tcx.is_impl_trait_in_trait(def_id)
if let ty::Alias(alias) = *ty.kind()
&& let Some(proj) = alias.try_to_projection()
&& self.tcx.is_impl_trait_in_trait(proj.kind)
{
if self.types.insert(proj) {
for (pred, _) in self
.tcx
.explicit_item_bounds(def_id)
.explicit_item_bounds(proj.kind)
.iter_instantiated_copied(self.tcx, proj.args)
.map(Unnormalized::skip_norm_wip)
{
Expand Down
14 changes: 2 additions & 12 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::print::with_types_for_signature;
use rustc_middle::ty::{
self, GenericArgs, GenericArgsRef, OutlivesPredicate, Region, Ty, TyCtxt, TypingMode,
Unnormalized,
};
use rustc_middle::{bug, span_bug};
use rustc_session::errors::feature_err;
Expand Down Expand Up @@ -487,21 +486,12 @@ fn fn_sig_suggestion<'tcx>(
let mut output = sig.output();

let asyncness = if tcx.asyncness(assoc.def_id).is_async() {
output = if let ty::Alias(alias_ty) = *output.kind()
&& let Some(output) = tcx
.explicit_item_self_bounds(alias_ty.kind.def_id())
.iter_instantiated_copied(tcx, alias_ty.args)
.map(Unnormalized::skip_norm_wip)
.find_map(|(bound, _)| {
bound.as_projection_clause()?.no_bound_vars()?.term.as_type()
}) {
output
} else {
output = tcx.get_impl_future_output_ty(output).unwrap_or_else(|| {
span_bug!(
ident.span,
"expected async fn to have `impl Future` output, but it returns {output}"
)
};
});
"async "
} else {
""
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,7 +1359,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
)? {
TypeRelativePath::AssocItem(alias_term) => {
let alias_ty = alias_term.expect_ty();
let def_id = alias_ty.kind.def_id();
let def_id = match alias_ty.kind {
ty::AliasTyKind::Projection { def_id } => def_id,
ty::AliasTyKind::Inherent { def_id } => def_id,
kind => bug!("expected projection or inherent alias, got {kind:?}"),
};
let ty = alias_ty.to_ty(tcx);
let ty = self.check_param_uses_if_mcg(ty, span, false);
Ok((ty, tcx.def_kind(def_id), def_id))
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2910,7 +2910,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base: &'tcx hir::Expr<'tcx>,
ty: Ty<'tcx>,
) {
let Some(output_ty) = self.err_ctxt().get_impl_future_output_ty(ty) else {
let Some(output_ty) = self.tcx.get_impl_future_output_ty(ty) else {
err.span_label(field_ident.span, "unknown field");
return;
};
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1351,7 +1351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
let ty = match self.tcx.asyncness(fn_id) {
ty::Asyncness::Yes => {
self.err_ctxt().get_impl_future_output_ty(ty).unwrap_or_else(|| {
self.tcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
span_bug!(
fn_decl.output.span(),
"failed to get output type of async function"
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3878,7 +3878,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
return_type: Option<Ty<'tcx>>,
) {
let Some(output_ty) = self.err_ctxt().get_impl_future_output_ty(ty) else { return };
let Some(output_ty) = self.tcx.get_impl_future_output_ty(ty) else { return };
let output_ty = self.resolve_vars_if_possible(output_ty);
let method_exists =
self.method_exists_for_diagnostic(item_name, output_ty, call.hir_id, return_type);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ 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::AliasTy<'tcx>> {
fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec<ty::OpaqueAliasTy<'tcx>> {
self.opaques_with_sub_unified_hidden_type(ty)
}

Expand Down
19 changes: 13 additions & 6 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1111,7 +1111,10 @@ impl<'tcx> InferCtxt<'tcx> {
/// Searches for an opaque type key whose hidden type is related to `ty_vid`.
///
/// This only checks for a subtype relation, it does not require equality.
pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec<ty::AliasTy<'tcx>> {
pub fn opaques_with_sub_unified_hidden_type(
&self,
ty_vid: TyVid,
) -> Vec<ty::OpaqueAliasTy<'tcx>> {
// Avoid accidentally allowing more code to compile with the old solver.
if !self.next_trait_solver() {
return vec![];
Expand All @@ -1129,11 +1132,15 @@ impl<'tcx> InferCtxt<'tcx> {
if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() {
let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid);
if opaque_sub_vid == ty_sub_vid {
return Some(ty::AliasTy::new_from_args(
self.tcx,
ty::Opaque { def_id: key.def_id.into() },
key.args,
));
return Some(
ty::AliasTy::new_from_args(
self.tcx,
ty::Opaque { def_id: key.def_id.into() },
key.args,
)
.try_to_opaque()

@lcnr lcnr Jun 17, 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.

hmm, why this indirection?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

there is currently no way to construct a OpaqueAliasTy directly (in particular, I want to make sure debug_assert_args_compatible is called).

  • option 1: make a bespoke constructor for OpaqueAliasTy (and optionally Projection/Inherent/Free too, depending on the policy on unused code, I'm not sure)
  • option 2: some kind of generic constructor, hmm. Maybe one that takes T: Into<AliasTerm>, and then call an AliasTerm-based debug_assert_args_compatible?
  • option 3: hit it with a stupid hammer and use the AliasTy constructor and .try_to_opaque()

I went with option 3 :s (option 1 I was nervous about unused code, option 2 runs into the issue that we talked about of I::OpaqueTyId not being a newtype wrapper - I::OpaqueTyId can't impl Into<AliasTerm> at the moment, it's just a DefId). Happy to refactor, I just dunno what the nicest thing to do is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think that 1 is fine, id personally kinda bias towards adding all the dead code for the other kinds of aliases just so it's less annoying when they do actually need to be used, but it's also fine to only add the stuff actually needed 😌

.unwrap(),
);
}
}

Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_infer/src/infer/outlives/for_liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,14 @@ where
ty::Alias(ty::AliasTy { kind, args, .. }) => {
let tcx = self.tcx;
let param_env = self.param_env;
let def_id = match kind {
ty::AliasTyKind::Projection { def_id }
| ty::AliasTyKind::Inherent { def_id }
| ty::AliasTyKind::Opaque { def_id }
| ty::AliasTyKind::Free { def_id } => def_id,
};
let outlives_bounds: Vec<_> = tcx
.item_bounds(kind.def_id())
.item_bounds(def_id)
.iter_instantiated(tcx, args)
.map(Unnormalized::skip_norm_wip)
.chain(param_env.caller_bounds())
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_infer/src/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
// And therefore we can safely use structural equality for alias types.
(GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {}
(GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {}
(GenericKind::Alias(a1), ty::Alias(a2)) if a1.kind.def_id() == a2.kind.def_id() => {
}
(GenericKind::Alias(a1), ty::Alias(a2)) if a1.kind == a2.kind => {}
_ => return None,
}

Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_middle/src/query/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,12 @@ fn def_id_of_type_cached<'a>(ty: Ty<'a>, visited: &mut SsoHashSet<Ty<'a>>) -> Op
| ty::CoroutineWitness(def_id, _)
| ty::Foreign(def_id) => Some(def_id),

ty::Alias(alias) => Some(alias.kind.def_id()),
ty::Alias(alias) => match alias.kind {
ty::AliasTyKind::Projection { def_id }
| ty::AliasTyKind::Inherent { def_id }
| ty::AliasTyKind::Opaque { def_id }
| ty::AliasTyKind::Free { def_id } => Some(def_id),
},

ty::Bool
| ty::Char
Expand Down
33 changes: 33 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2704,6 +2704,39 @@ impl<'tcx> TyCtxt<'tcx> {
self.opt_rpitit_info(def_id).is_some()
}

pub fn get_impl_future_output_ty(self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {

@khyperia khyperia Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I moved this from TypeErrCtxt to TyCtxt... maybe I should split this to another commit

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could be nice yeah, but also was fine as is for me when reviewing

let (def_id, args) = match *ty.kind() {
ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => (def_id, args),
ty::Alias(ty::AliasTy { kind: ty::Projection { def_id }, args, .. })
if self.is_impl_trait_in_trait(def_id) =>
{
(def_id, args)
}
_ => return None,
};

let future_trait = self.require_lang_item(LangItem::Future, DUMMY_SP);
let item_def_id = self.associated_item_def_ids(future_trait)[0];

self.explicit_item_self_bounds(def_id)
.iter_instantiated_copied(self, args)
.map(ty::Unnormalized::skip_norm_wip)
.find_map(|(predicate, _)| {
predicate
.kind()
.map_bound(|kind| match kind {
ty::ClauseKind::Projection(projection_predicate)
if projection_predicate.def_id() == item_def_id =>
{
projection_predicate.term.as_type()
}
_ => None,
})
.no_bound_vars()
.flatten()
})
}

/// Named module children from all kinds of items, including imports.
/// In addition to regular items this list also includes struct and variant constructors, and
/// items inside `extern {}` blocks because all of them introduce names into parent module.
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,12 @@ pub use self::region::{
EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionKind, RegionVid,
};
pub use self::sty::{
AliasTy, AliasTyKind, Article, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy,
BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, FnSig,
FnSigKind, InlineConstArgs, InlineConstArgsParts, ParamConst, ParamTy, PlaceholderConst,
PlaceholderRegion, PlaceholderType, PolyFnSig, TyKind, TypeAndMut, TypingMode,
TypingModeEqWrapper, Unnormalized, UpvarArgs,
Alias, AliasTy, AliasTyKind, Article, Binder, BoundConst, BoundRegion, BoundRegionKind,
BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder,
FnSig, FnSigKind, FreeAliasTy, InherentAliasTy, InlineConstArgs, InlineConstArgsParts,
OpaqueAliasTy, ParamConst, ParamTy, PlaceholderConst, PlaceholderRegion, PlaceholderType,
PolyFnSig, ProjectionAliasTy, TyKind, TypeAndMut, TypingMode, TypingModeEqWrapper,
Unnormalized, UpvarArgs,
};
pub use self::trait_def::TraitDef;
pub use self::typeck_results::{
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,10 +1369,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
self.tcx().opt_rpitit_info(def_id)
&& let ty::Alias(alias_ty) =
self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind()
&& alias_ty.kind.def_id() == def_id
&& let Some(projection_ty) = alias_ty.try_to_projection()
&& projection_ty.kind == def_id
&& let generics = self.tcx().generics_of(fn_def_id)
// FIXME(return_type_notation): We only support lifetime params for now.
&& generics.own_params.iter().all(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime))
&& generics
.own_params
.iter()
.all(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime))
{
let num_args = generics.count();
Some((fn_def_id, &args[..num_args]))
Expand Down
27 changes: 21 additions & 6 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ pub type TyKind<'tcx> = ir::TyKind<TyCtxt<'tcx>>;
pub type TypeAndMut<'tcx> = ir::TypeAndMut<TyCtxt<'tcx>>;
pub type AliasTy<'tcx> = ir::AliasTy<TyCtxt<'tcx>>;
pub type AliasTyKind<'tcx> = ir::AliasTyKind<TyCtxt<'tcx>>;
pub type Alias<'tcx, K> = ir::Alias<TyCtxt<'tcx>, K>;
pub type ProjectionAliasTy<'tcx> = ir::ProjectionAliasTy<TyCtxt<'tcx>>;
pub type InherentAliasTy<'tcx> = ir::InherentAliasTy<TyCtxt<'tcx>>;
pub type OpaqueAliasTy<'tcx> = ir::OpaqueAliasTy<TyCtxt<'tcx>>;
pub type FreeAliasTy<'tcx> = ir::FreeAliasTy<TyCtxt<'tcx>>;

@khyperia khyperia Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

some of these are unused... I'm not sure what the preference is here, if we should have unused useful things that fill out a "pattern", or if we should strictly add only what is actually used. (same question for try_to_inherent and stuff)

View changes since the review

pub type FnSig<'tcx> = ir::FnSig<TyCtxt<'tcx>>;
pub type FnSigKind<'tcx> = ir::FnSigKind<TyCtxt<'tcx>>;
pub type Binder<'tcx, T> = ir::Binder<TyCtxt<'tcx>, T>;
Expand Down Expand Up @@ -477,12 +482,22 @@ impl<'tcx> Ty<'tcx> {

#[inline]
pub fn new_alias(tcx: TyCtxt<'tcx>, alias_ty: ty::AliasTy<'tcx>) -> Ty<'tcx> {
debug_assert_matches!(
(alias_ty.kind, tcx.def_kind(alias_ty.kind.def_id())),
(ty::Opaque { .. }, DefKind::OpaqueTy)
| (ty::Projection { .. } | ty::Inherent { .. }, DefKind::AssocTy)
| (ty::Free { .. }, DefKind::TyAlias)
);
if cfg!(debug_assertions) {
match alias_ty.kind {
ty::AliasTyKind::Projection { def_id } => {
debug_assert_matches!(tcx.def_kind(def_id), DefKind::AssocTy)
}
ty::AliasTyKind::Inherent { def_id } => {
debug_assert_matches!(tcx.def_kind(def_id), DefKind::AssocTy)
}
ty::AliasTyKind::Opaque { def_id } => {
debug_assert_matches!(tcx.def_kind(def_id), DefKind::OpaqueTy)
}
ty::AliasTyKind::Free { def_id } => {
debug_assert_matches!(tcx.def_kind(def_id), DefKind::TyAlias)
}
}
}
Ty::new(tcx, Alias(alias_ty))
}

Expand Down
Loading
Loading