Auto merge of #17845 - ShoyuVanilla:tait, r=Veykril

feat: Implement TAIT and fix ATPIT a bit

Closes #16296 (Commented on the issue)

In #16852, I implemented ATPIT, but as I didn't discern ATPIT and other non-assoc TAIT, I guess that it has been working for some TAITs.

As the definining usage of TAIT requires it should be appear in the Def body's type(const blocks' type annotations or functions' signatures), this can be done in simlilar way with ATPIT

And this PR also corrects some defining-usage resolution for ATPIT
This commit is contained in:
bors 2024-08-12 08:08:40 +00:00
commit 3ef56c2bb8
14 changed files with 291 additions and 191 deletions

View File

@ -160,7 +160,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
fn const_data(&self, konst: ConstId) -> Arc<ConstData>; fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
#[salsa::invoke(StaticData::static_data_query)] #[salsa::invoke(StaticData::static_data_query)]
fn static_data(&self, konst: StaticId) -> Arc<StaticData>; fn static_data(&self, statik: StaticId) -> Arc<StaticData>;
#[salsa::invoke(Macro2Data::macro2_data_query)] #[salsa::invoke(Macro2Data::macro2_data_query)]
fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>; fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;

View File

@ -275,7 +275,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
}; };
chalk_ir::Binders::new(binders, bound) chalk_ir::Binders::new(binders, bound)
} }
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas = self let datas = self
.db .db
.type_alias_impl_traits(alias) .type_alias_impl_traits(alias)

View File

@ -276,7 +276,7 @@ impl TyExt for Ty {
data.substitute(Interner, &subst).into_value_and_skipped_binders().0 data.substitute(Interner, &subst).into_value_and_skipped_binders().0
}) })
} }
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { ImplTraitId::TypeAliasImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| { db.type_alias_impl_traits(alias).map(|it| {
let data = let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
@ -295,7 +295,7 @@ impl TyExt for Ty {
data.substitute(Interner, &opaque_ty.substitution) data.substitute(Interner, &opaque_ty.substitution)
}) })
} }
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { ImplTraitId::TypeAliasImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| { db.type_alias_impl_traits(alias).map(|it| {
let data = let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());

View File

@ -1152,11 +1152,10 @@ impl HirDisplay for Ty {
)?; )?;
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
} }
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas = let datas =
db.type_alias_impl_traits(alias).expect("impl trait id without data"); db.type_alias_impl_traits(alias).expect("impl trait id without data");
let data = let data = (*datas).as_ref().map(|it| it.impl_traits[idx].bounds.clone());
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
let bounds = data.substitute(Interner, &parameters); let bounds = data.substitute(Interner, &parameters);
let krate = alias.krate(db.upcast()); let krate = alias.krate(db.upcast());
write_bounds_like_dyn_trait_with_prefix( write_bounds_like_dyn_trait_with_prefix(
@ -1339,7 +1338,7 @@ impl HirDisplay for Ty {
SizedByDefault::Sized { anchor: krate }, SizedByDefault::Sized { anchor: krate },
)?; )?;
} }
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas = let datas =
db.type_alias_impl_traits(alias).expect("impl trait id without data"); db.type_alias_impl_traits(alias).expect("impl trait id without data");
let data = let data =

View File

@ -36,15 +36,14 @@ use hir_def::{
body::Body, body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData}, data::{ConstData, StaticData},
hir::LabelId, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
layout::Integer, layout::Integer,
path::{ModPath, Path}, path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::{LifetimeRef, TypeRef}, type_ref::{LifetimeRef, TypeRef},
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup,
TupleFieldId, TupleId, TypeAliasId, VariantId, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use indexmap::IndexSet; use indexmap::IndexSet;
@ -785,14 +784,19 @@ impl<'a> InferenceContext<'a> {
fn collect_const(&mut self, data: &ConstData) { fn collect_const(&mut self, data: &ConstData) {
let return_ty = self.make_ty(&data.type_ref); let return_ty = self.make_ty(&data.type_ref);
// Constants might be associated items that define ATPITs. // Constants might be defining usage sites of TAITs.
self.insert_atpit_coercion_table(iter::once(&return_ty)); self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty; self.return_ty = return_ty;
} }
fn collect_static(&mut self, data: &StaticData) { fn collect_static(&mut self, data: &StaticData) {
self.return_ty = self.make_ty(&data.type_ref); let return_ty = self.make_ty(&data.type_ref);
// Statics might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
} }
fn collect_fn(&mut self, func: FunctionId) { fn collect_fn(&mut self, func: FunctionId) {
@ -857,11 +861,11 @@ impl<'a> InferenceContext<'a> {
self.return_ty = self.normalize_associated_types_in(return_ty); self.return_ty = self.normalize_associated_types_in(return_ty);
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
// Functions might be associated items that define ATPITs. // Functions might be defining usage sites of TAITs.
// To define an ATPITs, that ATPIT must appear in the function's signatures. // To define an TAITs, that TAIT must appear in the function's signatures.
// So, it suffices to check for params and return types. // So, it suffices to check for params and return types.
params_and_ret_tys.push(self.return_ty.clone()); params_and_ret_tys.push(self.return_ty.clone());
self.insert_atpit_coercion_table(params_and_ret_tys.iter()); self.make_tait_coercion_table(params_and_ret_tys.iter());
} }
fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
@ -880,7 +884,7 @@ impl<'a> InferenceContext<'a> {
ImplTraitId::ReturnTypeImplTrait(def, idx) => { ImplTraitId::ReturnTypeImplTrait(def, idx) => {
(self.db.return_type_impl_traits(def), idx) (self.db.return_type_impl_traits(def), idx)
} }
ImplTraitId::AssociatedTypeImplTrait(def, idx) => { ImplTraitId::TypeAliasImplTrait(def, idx) => {
(self.db.type_alias_impl_traits(def), idx) (self.db.type_alias_impl_traits(def), idx)
} }
_ => unreachable!(), _ => unreachable!(),
@ -909,23 +913,25 @@ impl<'a> InferenceContext<'a> {
} }
/// The coercion of a non-inference var into an opaque type should fail, /// The coercion of a non-inference var into an opaque type should fail,
/// but not in the defining sites of the ATPITs. /// but not in the defining sites of the TAITs.
/// In such cases, we insert an proxy inference var for each ATPIT, /// In such cases, we insert an proxy inference var for each TAIT,
/// and coerce into it instead of ATPIT itself. /// and coerce into it instead of TAIT itself.
/// ///
/// The inference var stretagy is effective because; /// The inference var stretagy is effective because;
/// ///
/// - It can still unify types that coerced into ATPIT /// - It can still unify types that coerced into TAITs
/// - We are pushing `impl Trait` bounds into it /// - We are pushing `impl Trait` bounds into it
/// ///
/// This function inserts a map that maps the opaque type to that proxy inference var. /// This function inserts a map that maps the opaque type to that proxy inference var.
fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator<Item = &'b Ty>) { fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator<Item = &'b Ty>) {
struct OpaqueTyCollector<'a, 'b> { struct TypeAliasImplTraitCollector<'a, 'b> {
db: &'b dyn HirDatabase,
table: &'b mut InferenceTable<'a>, table: &'b mut InferenceTable<'a>,
opaque_tys: FxHashMap<OpaqueTyId, Ty>, assocs: FxHashMap<OpaqueTyId, (ImplId, Ty)>,
non_assocs: FxHashMap<OpaqueTyId, Ty>,
} }
impl<'a, 'b> TypeVisitor<Interner> for OpaqueTyCollector<'a, 'b> { impl<'a, 'b> TypeVisitor<Interner> for TypeAliasImplTraitCollector<'a, 'b> {
type BreakTy = (); type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> { fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
@ -944,59 +950,105 @@ impl<'a> InferenceContext<'a> {
let ty = self.table.resolve_ty_shallow(ty); let ty = self.table.resolve_ty_shallow(ty);
if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
self.opaque_tys.insert(*id, ty.clone()); if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id((*id).into())
{
let loc = self.db.lookup_intern_type_alias(alias_id);
match loc.container {
ItemContainerId::ImplId(impl_id) => {
self.assocs.insert(*id, (impl_id, ty.clone()));
}
ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => {
self.non_assocs.insert(*id, ty.clone());
}
_ => {}
}
}
} }
ty.super_visit_with(self, outer_binder) ty.super_visit_with(self, outer_binder)
} }
} }
// Early return if this is not happening inside the impl block let mut collector = TypeAliasImplTraitCollector {
let impl_id = if let Some(impl_id) = self.resolver.impl_def() { db: self.db,
impl_id table: &mut self.table,
} else { assocs: FxHashMap::default(),
return; non_assocs: FxHashMap::default(),
}; };
for ty in tait_candidates {
let assoc_tys: FxHashSet<_> = self
.db
.impl_data(impl_id)
.items
.iter()
.filter_map(|item| match item {
AssocItemId::TypeAliasId(alias) => Some(*alias),
_ => None,
})
.collect();
if assoc_tys.is_empty() {
return;
}
let mut collector =
OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() };
for ty in tys {
ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
} }
let atpit_coercion_table: FxHashMap<_, _> = collector
.opaque_tys
.into_iter()
.filter_map(|(opaque_ty_id, ty)| {
if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
{
if assoc_tys.contains(&alias_id) {
let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(ty, alias_placeholders);
return Some((opaque_ty_id, ty));
}
}
None // Non-assoc TAITs can be define-used everywhere as long as they are
// in function signatures or const types, etc
let mut taits = collector.non_assocs;
// assoc TAITs(ATPITs) can be only define-used inside their impl block.
// They cannot be define-used in inner items like in the following;
//
// ```
// impl Trait for Struct {
// type Assoc = impl Default;
//
// fn assoc_fn() -> Self::Assoc {
// let foo: Self::Assoc = true; // Allowed here
//
// fn inner() -> Self::Assoc {
// false // Not allowed here
// }
//
// foo
// }
// }
// ```
let impl_id = match self.owner {
DefWithBodyId::FunctionId(it) => {
let loc = self.db.lookup_intern_function(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
DefWithBodyId::ConstId(it) => {
let loc = self.db.lookup_intern_const(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
_ => None,
};
if let Some(impl_id) = impl_id {
taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| {
if impl_ == impl_id {
Some((id, ty))
} else {
None
}
}));
}
let tait_coercion_table: FxHashMap<_, _> = taits
.into_iter()
.filter_map(|(id, ty)| {
if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id(id.into())
{
let subst = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(ty, subst);
Some((id, ty))
} else {
None
}
}) })
.collect(); .collect();
if !atpit_coercion_table.is_empty() { if !tait_coercion_table.is_empty() {
self.table.atpit_coercion_table = Some(atpit_coercion_table); self.table.tait_coercion_table = Some(tait_coercion_table);
} }
} }

View File

@ -276,16 +276,16 @@ impl InferenceTable<'_> {
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
} }
// If we are coercing into an ATPIT, coerce into its proxy inference var, instead. // If we are coercing into a TAIT, coerce into its proxy inference var, instead.
let mut to_ty = to_ty; let mut to_ty = to_ty;
let _to; let _to;
if let Some(atpit_table) = &self.atpit_coercion_table { if let Some(tait_table) = &self.tait_coercion_table {
if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) {
if !matches!( if !matches!(
from_ty.kind(Interner), from_ty.kind(Interner),
TyKind::InferenceVar(..) | TyKind::OpaqueType(..) TyKind::InferenceVar(..) | TyKind::OpaqueType(..)
) { ) {
if let Some(ty) = atpit_table.get(opaque_ty_id) { if let Some(ty) = tait_table.get(opaque_ty_id) {
_to = ty.clone(); _to = ty.clone();
to_ty = &_to; to_ty = &_to;
} }

View File

@ -224,7 +224,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
pub(crate) struct InferenceTable<'a> { pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase, pub(crate) db: &'a dyn HirDatabase,
pub(crate) trait_env: Arc<TraitEnvironment>, pub(crate) trait_env: Arc<TraitEnvironment>,
pub(crate) atpit_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>, pub(crate) tait_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
var_unification_table: ChalkInferenceTable, var_unification_table: ChalkInferenceTable,
type_variable_table: SmallVec<[TypeVariableFlags; 16]>, type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
@ -244,7 +244,7 @@ impl<'a> InferenceTable<'a> {
InferenceTable { InferenceTable {
db, db,
trait_env, trait_env,
atpit_coercion_table: None, tait_coercion_table: None,
var_unification_table: ChalkInferenceTable::new(), var_unification_table: ChalkInferenceTable::new(),
type_variable_table: SmallVec::new(), type_variable_table: SmallVec::new(),
pending_obligations: Vec::new(), pending_obligations: Vec::new(),

View File

@ -391,7 +391,7 @@ pub fn layout_of_ty_query(
let infer = db.infer(func.into()); let infer = db.infer(func.into());
return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
} }
crate::ImplTraitId::AssociatedTypeImplTrait(..) => { crate::ImplTraitId::TypeAliasImplTrait(..) => {
return Err(LayoutError::NotImplemented); return Err(LayoutError::NotImplemented);
} }
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {

View File

@ -595,7 +595,7 @@ impl TypeFoldable<Interner> for CallableSig {
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ImplTraitId { pub enum ImplTraitId {
ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx), TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
} }
impl InternValueTrivial for ImplTraitId {} impl InternValueTrivial for ImplTraitId {}

View File

@ -341,7 +341,7 @@ impl<'a> TyLoweringContext<'a> {
let impl_trait_id = origin.either( let impl_trait_id = origin.either(
|f| ImplTraitId::ReturnTypeImplTrait(f, idx), |f| ImplTraitId::ReturnTypeImplTrait(f, idx),
|a| ImplTraitId::AssociatedTypeImplTrait(a, idx), |a| ImplTraitId::TypeAliasImplTrait(a, idx),
); );
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
let generics = let generics =
@ -2131,7 +2131,6 @@ pub(crate) fn type_alias_impl_traits(
if let Some(type_ref) = &data.type_ref { if let Some(type_ref) = &data.type_ref {
let _ty = ctx.lower_ty(type_ref); let _ty = ctx.lower_ty(type_ref);
} }
let generics = generics(db.upcast(), def.into());
let type_alias_impl_traits = ImplTraits { let type_alias_impl_traits = ImplTraits {
impl_traits: match ctx.impl_trait_mode { impl_traits: match ctx.impl_trait_mode {
ImplTraitLoweringState::Opaque(x) => x.into_inner(), ImplTraitLoweringState::Opaque(x) => x.into_inner(),
@ -2141,6 +2140,7 @@ pub(crate) fn type_alias_impl_traits(
if type_alias_impl_traits.impl_traits.is_empty() { if type_alias_impl_traits.impl_traits.is_empty() {
None None
} else { } else {
let generics = generics(db.upcast(), def.into());
Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits)))
} }
} }

View File

@ -82,8 +82,8 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
}; };
filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
} }
crate::ImplTraitId::AssociatedTypeImplTrait(..) => { crate::ImplTraitId::TypeAliasImplTrait(..) => {
not_supported!("associated type impl trait"); not_supported!("type alias impl trait");
} }
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
not_supported!("async block impl trait"); not_supported!("async block impl trait");

View File

@ -9,6 +9,7 @@ mod patterns;
mod regression; mod regression;
mod simple; mod simple;
mod traits; mod traits;
mod type_alias_impl_traits;
use std::env; use std::env;

View File

@ -4691,119 +4691,6 @@ fn f<T: Send, U>() {
); );
} }
#[test]
fn associated_type_impl_trait() {
check_types(
r#"
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Self::Item;
}
struct S2;
impl Bar for S2 {
type Item = impl Foo;
fn bar(&self) -> Self::Item {
S1
}
}
fn test() {
let x = S2.bar();
//^ impl Foo + ?Sized
}
"#,
);
}
#[test]
fn associated_type_impl_traits_complex() {
check_types(
r#"
struct Unary<T>(T);
struct Binary<T, U>(T, U);
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Unary<Self::Item>;
}
struct S2;
impl Bar for S2 {
type Item = Unary<impl Foo>;
fn bar(&self) -> Unary<<Self as Bar>::Item> {
Unary(Unary(S1))
}
}
trait Baz {
type Target1;
type Target2;
fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
}
struct S3;
impl Baz for S3 {
type Target1 = impl Foo;
type Target2 = Unary<impl Bar>;
fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
Binary(S1, Unary(S2))
}
}
fn test() {
let x = S3.baz();
//^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
let y = x.1.0.bar();
//^ Unary<Bar::Item<impl Bar + ?Sized>>
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_nested_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test] #[test]
fn dyn_trait_with_lifetime_in_rpit() { fn dyn_trait_with_lifetime_in_rpit() {
check_types( check_types(

View File

@ -0,0 +1,161 @@
use expect_test::expect;
use super::{check_infer_with_mismatches, check_no_mismatches, check_types};
#[test]
fn associated_type_impl_trait() {
check_types(
r#"
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Self::Item;
}
struct S2;
impl Bar for S2 {
type Item = impl Foo;
fn bar(&self) -> Self::Item {
S1
}
}
fn test() {
let x = S2.bar();
//^ impl Foo + ?Sized
}
"#,
);
}
#[test]
fn associated_type_impl_traits_complex() {
check_types(
r#"
struct Unary<T>(T);
struct Binary<T, U>(T, U);
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Unary<Self::Item>;
}
struct S2;
impl Bar for S2 {
type Item = Unary<impl Foo>;
fn bar(&self) -> Unary<<Self as Bar>::Item> {
Unary(Unary(S1))
}
}
trait Baz {
type Target1;
type Target2;
fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
}
struct S3;
impl Baz for S3 {
type Target1 = impl Foo;
type Target2 = Unary<impl Bar>;
fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
Binary(S1, Unary(S2))
}
}
fn test() {
let x = S3.baz();
//^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
let y = x.1.0.bar();
//^ Unary<Bar::Item<impl Bar + ?Sized>>
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_nested_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn type_alias_impl_trait_simple() {
check_no_mismatches(
r#"
trait Trait {}
struct Struct;
impl Trait for Struct {}
type AliasTy = impl Trait;
static ALIAS: AliasTy = {
let res: AliasTy = Struct;
res
};
"#,
);
check_infer_with_mismatches(
r#"
trait Trait {}
struct Struct;
impl Trait for Struct {}
type AliasTy = impl Trait;
static ALIAS: i32 = {
// TATIs cannot be define-used if not in signature or type annotations
let _a: AliasTy = Struct;
5
};
"#,
expect![[r#"
106..220 '{ ... 5 }': i32
191..193 '_a': impl Trait + ?Sized
205..211 'Struct': Struct
217..218 '5': i32
205..211: expected impl Trait + ?Sized, got Struct
"#]],
)
}