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
84 changes: 81 additions & 3 deletions crates/hir-ty/src/infer/opaques.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
//! Defining opaque types via inference.

use rustc_type_ir::{TypeVisitableExt, fold_regions};
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
GenericArgKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, fold_regions,
inherent::{GenericArgs as _, IntoKind},
};
use tracing::{debug, instrument};

use crate::{
Span,
infer::InferenceContext,
next_solver::{
EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode,
Const, ConstKind, DbInterner, EarlyBinder, GenericArg, GenericArgs, OpaqueTypeKey, Region,
RegionKind, SolverDefId, Ty, TyKind, TypingMode,
infer::{opaque_types::OpaqueHiddenType, traits::ObligationCause},
},
};
Expand All @@ -31,7 +36,7 @@ impl<'db> InferenceContext<'db> {
}
}

#[expect(unused, reason = "rustc has this")]
#[expect(dead_code, reason = "payloads are retained for non-defining-use diagnostics")]
#[derive(Copy, Clone, Debug)]
enum UsageKind<'db> {
None,
Expand Down Expand Up @@ -63,6 +68,73 @@ impl<'db> UsageKind<'db> {
}
}

struct ReverseMapper<'db> {
interner: DbInterner<'db>,
map: FxHashMap<GenericArg<'db>, GenericArg<'db>>,
}

impl<'db> ReverseMapper<'db> {
fn new(interner: DbInterner<'db>, opaque_type_key: OpaqueTypeKey<'db>) -> Self {
let id_args = GenericArgs::identity_for_item(interner, opaque_type_key.def_id.into());
let map = opaque_type_key.args.iter().zip(id_args.iter()).collect();
Self { interner, map }
}
}

impl<'db> TypeFolder<DbInterner<'db>> for ReverseMapper<'db> {
fn cx(&self) -> DbInterner<'db> {
self.interner
}

fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
match ty.kind() {
// A parameter outside the opaque declaration is malformed input. Keep the
// recovered type foldable so the caller never passes it to an incompatible binder.
TyKind::Param(_) => match self.map.get(&ty.into()).map(|arg| arg.kind()) {
Some(GenericArgKind::Type(ty)) => ty,
Some(_) | None => self.interner.default_types().types.error,
},
_ => ty.super_fold_with(self),
}
}

fn fold_const(&mut self, konst: Const<'db>) -> Const<'db> {
match konst.kind() {
ConstKind::Param(_) => match self.map.get(&konst.into()).map(|arg| arg.kind()) {
Some(GenericArgKind::Const(konst)) => konst,
Some(_) | None => self.interner.default_types().consts.error,
},
_ => konst.super_fold_with(self),
}
}

fn fold_region(&mut self, region: Region<'db>) -> Region<'db> {
match region.kind() {
RegionKind::ReEarlyParam(_) | RegionKind::ReLateParam(_) => {
match self.map.get(&region.into()).map(|arg| arg.kind()) {
Some(GenericArgKind::Lifetime(region)) => region,
Some(_) | None => self.interner.default_types().regions.erased,
}
}
_ => region,
}
}
}

fn opaque_type_has_defining_use_args<'db>(
interner: DbInterner<'db>,
opaque_type_key: OpaqueTypeKey<'db>,
) -> bool {
let mut seen = FxHashSet::default();
opaque_type_key.iter_captured_args(interner).all(|(_, arg)| match arg.kind() {
GenericArgKind::Lifetime(_) => true,
GenericArgKind::Type(ty) => matches!(ty.kind(), TyKind::Param(_)) && seen.insert(arg),
GenericArgKind::Const(konst) => {
matches!(konst.kind(), ConstKind::Param(_)) && seen.insert(arg)
}
})
}

impl<'db> InferenceContext<'db> {
fn compute_definition_site_hidden_types(
&mut self,
Expand Down Expand Up @@ -129,6 +201,10 @@ impl<'db> InferenceContext<'db> {
opaque_type_key: OpaqueTypeKey<'db>,
hidden_type: OpaqueHiddenType<'db>,
) -> UsageKind<'db> {
if !opaque_type_has_defining_use_args(self.interner(), opaque_type_key) {
return UsageKind::NonDefiningUse(opaque_type_key, hidden_type);
}

// We ignore uses of the opaque if they have any inference variables
// as this can frequently happen with recursive calls.
//
Expand All @@ -146,6 +222,8 @@ impl<'db> InferenceContext<'db> {
};
let hidden_type =
fold_regions(self.interner(), hidden_type, |_, _| self.types.regions.erased);
let hidden_type =
hidden_type.fold_with(&mut ReverseMapper::new(self.interner(), opaque_type_key));
UsageKind::HasDefiningUse(hidden_type)
}
}
68 changes: 68 additions & 0 deletions crates/hir-ty/src/tests/regression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3193,3 +3193,71 @@ fn main() {
"#,
);
}

#[test]
fn issue_23124_atpit_hidden_type_with_method_generic_does_not_panic() {
check_no_mismatches(
r#"
//- minicore: copy
trait Bar {
type E: Copy;

fn foo<T>() -> Self::E;
}

impl<S> Bar for S {
type E = impl Copy;

fn foo<T>() -> Self::E {
|| ()
}
}

trait ValidBar {
type E: Copy;

fn foo() -> Self::E;
}

impl<S> ValidBar for S {
type E = impl Copy;

fn foo() -> Self::E {
|| ()
}
}
"#,
);

check_no_mismatches(
r#"
//- minicore: sized
fn recursive<T>() -> impl Sized {
// A concrete recursive call is a non-defining opaque use.
recursive::<u8>();
1u16
}
"#,
);

check_no_mismatches(
r#"
//- minicore: sized
fn only_nondefining<T>() -> impl Sized {
// There is no defining use, but this must still recover without a panic.
only_nondefining::<u8>();
loop {}
}
"#,
);

check_no_mismatches(
r#"
//- minicore: sized
fn const_nondefining<const N: usize>() -> impl Sized {
const_nondefining::<1>();
1u16
}
"#,
);
}