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
53 changes: 50 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 std::ops::ControlFlow;

use rustc_type_ir::{
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, fold_regions, inherent::IntoKind,
};
use tracing::{debug, instrument};

use crate::{
Span,
infer::InferenceContext,
next_solver::{
EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode,
Const, ConstKind, DbInterner, EarlyBinder, GenericArgKind, GenericArgs, OpaqueTypeKey,
SolverDefId, Ty, TyKind, TypingMode,
infer::{opaque_types::OpaqueHiddenType, traits::ObligationCause},
},
};
Expand Down Expand Up @@ -63,6 +68,39 @@ impl<'db> UsageKind<'db> {
}
}

// rejects hidden types with foreign params
struct ForeignParamChecker<'db> {
args: GenericArgs<'db>,
}

impl<'db> rustc_type_ir::TypeVisitor<DbInterner<'db>> for ForeignParamChecker<'db> {
type Result = ControlFlow<()>;

fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
if let TyKind::Param(param) = ty.kind()
&& !matches!(
self.args.get(param.index as usize).map(|arg| arg.kind()),
Some(GenericArgKind::Type(_))
)
{
return ControlFlow::Break(());
}
ty.super_visit_with(self)
}

fn visit_const(&mut self, ct: Const<'db>) -> Self::Result {
if let ConstKind::Param(param) = ct.kind()
&& !matches!(
self.args.get(param.index as usize).map(|arg| arg.kind()),
Some(GenericArgKind::Const(_))
)
{
return ControlFlow::Break(());
}
ct.super_visit_with(self)
}
}

impl<'db> InferenceContext<'db> {
fn compute_definition_site_hidden_types(
&mut self,
Expand Down Expand Up @@ -115,7 +153,6 @@ impl<'db> InferenceContext<'db> {
}

self.result.type_of_opaque.insert(def_id, ty.ty.store());

continue;
}

Expand Down Expand Up @@ -146,6 +183,16 @@ impl<'db> InferenceContext<'db> {
};
let hidden_type =
fold_regions(self.interner(), hidden_type, |_, _| self.types.regions.erased);

// skip uses with foreign params
if hidden_type
.ty
.visit_with(&mut ForeignParamChecker { args: opaque_type_key.args })
.is_break()
{
return UsageKind::NonDefiningUse(opaque_type_key, hidden_type);
}

UsageKind::HasDefiningUse(hidden_type)
}
}
104 changes: 104 additions & 0 deletions crates/hir-ty/src/tests/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,87 @@ fn test() {
);
}

#[test]
fn regression_23125_atpit_with_method_generics() {
check_no_mismatches(
r#"
trait Foo {
type Item;
}
struct S<T>;
struct S2;
impl Foo for S2 {
type Item = impl Sized;
fn foo<T>(_: T) -> Self::Item {
S::<T>
}
}
"#,
);
}

// added multi-method non-defining test
#[test]
fn regression_23125_atpit_method_local_generic_non_defining_use() {
check_no_mismatches(
r#"
trait Foo {
type Item;
}
struct S<T>;
struct S2;
impl Foo for S2 {
type Item = impl Sized;
fn foo<U>(_: U) -> Self::Item {
S::<U>
}
fn bar() -> Self::Item {
S::<()>
}
}
"#,
);
}

#[test]
fn regression_23125_atpit_with_method_generics_impl_generics() {
check_no_mismatches(
r#"
trait Foo {
type Item;
}
struct S<T>;
struct S2<T>(T);
impl<T> Foo for S2<T> {
type Item = impl Sized;
fn foo<U>(_: U) -> Self::Item {
S::<U>
}
}
"#,
);
}

// added impl-generic no-mismatch test
#[test]
fn regression_23125_atpit_impl_generics_are_defining_use() {
check_no_mismatches(
r#"
trait Foo {
type Item;
}
struct S<T>;
struct S2<T>(T);
impl<T> Foo for S2<T> {
type Item = impl Sized;
fn foo() -> Self::Item {
S::<T>
}
}
"#,
);
}

#[test]
fn associated_type_impl_traits_complex() {
check_types(
Expand Down Expand Up @@ -79,6 +160,29 @@ fn test() {
);
}

#[test]
fn rpit_with_fn_generics_is_defining_use() {
check_infer(
r#"
trait Foo {}
struct S;
impl Foo for S {}
struct Wrap<T>(T);
impl<T> Foo for Wrap<T> {}

fn foo<T>() -> impl Foo {
Wrap(T)
}
"#,
expect![[r#"
112..127 '{ Wrap(T) }': Wrap<{unknown}>
118..122 'Wrap': fn Wrap<{unknown}>({unknown}) -> Wrap<{unknown}>
118..125 'Wrap(T)': Wrap<{unknown}>
123..124 'T': {unknown}
"#]],
);
}

#[test]
fn associated_type_with_impl_trait_in_tuple() {
check_no_mismatches(
Expand Down