mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
feat: Implement TAIT
This commit is contained in:
parent
56f63dfd8a
commit
c530e21714
14 changed files with 291 additions and 191 deletions
|
@ -160,7 +160,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
|||
fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
|
||||
|
||||
#[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)]
|
||||
fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;
|
||||
|
|
|
@ -275,7 +275,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
|||
};
|
||||
chalk_ir::Binders::new(binders, bound)
|
||||
}
|
||||
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
|
||||
let datas = self
|
||||
.db
|
||||
.type_alias_impl_traits(alias)
|
||||
|
|
|
@ -276,7 +276,7 @@ impl TyExt for Ty {
|
|||
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| {
|
||||
let data =
|
||||
(*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)
|
||||
})
|
||||
}
|
||||
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
|
||||
db.type_alias_impl_traits(alias).map(|it| {
|
||||
let data =
|
||||
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
|
|
|
@ -1151,11 +1151,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
|
||||
}
|
||||
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
|
||||
let datas =
|
||||
db.type_alias_impl_traits(alias).expect("impl trait id without data");
|
||||
let data =
|
||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
let data = (*datas).as_ref().map(|it| it.impl_traits[idx].bounds.clone());
|
||||
let bounds = data.substitute(Interner, ¶meters);
|
||||
let krate = alias.krate(db.upcast());
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
|
@ -1338,7 +1337,7 @@ impl HirDisplay for Ty {
|
|||
SizedByDefault::Sized { anchor: krate },
|
||||
)?;
|
||||
}
|
||||
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
|
||||
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
|
||||
let datas =
|
||||
db.type_alias_impl_traits(alias).expect("impl trait id without data");
|
||||
let data =
|
||||
|
|
|
@ -36,15 +36,14 @@ use hir_def::{
|
|||
body::Body,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
data::{ConstData, StaticData},
|
||||
hir::LabelId,
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
layout::Integer,
|
||||
path::{ModPath, Path},
|
||||
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
type_ref::{LifetimeRef, TypeRef},
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId,
|
||||
TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup,
|
||||
TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use indexmap::IndexSet;
|
||||
|
@ -785,14 +784,19 @@ impl<'a> InferenceContext<'a> {
|
|||
fn collect_const(&mut self, data: &ConstData) {
|
||||
let return_ty = self.make_ty(&data.type_ref);
|
||||
|
||||
// Constants might be associated items that define ATPITs.
|
||||
self.insert_atpit_coercion_table(iter::once(&return_ty));
|
||||
// Constants might be defining usage sites of TAITs.
|
||||
self.make_tait_coercion_table(iter::once(&return_ty));
|
||||
|
||||
self.return_ty = return_ty;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -857,11 +861,11 @@ impl<'a> InferenceContext<'a> {
|
|||
self.return_ty = self.normalize_associated_types_in(return_ty);
|
||||
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
|
||||
|
||||
// Functions might be associated items that define ATPITs.
|
||||
// To define an ATPITs, that ATPIT must appear in the function's signatures.
|
||||
// Functions might be defining usage sites of TAITs.
|
||||
// To define an TAITs, that TAIT must appear in the function's signatures.
|
||||
// So, it suffices to check for params and return types.
|
||||
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
|
||||
|
@ -880,7 +884,7 @@ impl<'a> InferenceContext<'a> {
|
|||
ImplTraitId::ReturnTypeImplTrait(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)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -909,23 +913,25 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
|
||||
/// The coercion of a non-inference var into an opaque type should fail,
|
||||
/// but not in the defining sites of the ATPITs.
|
||||
/// In such cases, we insert an proxy inference var for each ATPIT,
|
||||
/// and coerce into it instead of ATPIT itself.
|
||||
/// but not in the defining sites of the TAITs.
|
||||
/// In such cases, we insert an proxy inference var for each TAIT,
|
||||
/// and coerce into it instead of TAIT itself.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// 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>) {
|
||||
struct OpaqueTyCollector<'a, 'b> {
|
||||
fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator<Item = &'b Ty>) {
|
||||
struct TypeAliasImplTraitCollector<'a, 'b> {
|
||||
db: &'b dyn HirDatabase,
|
||||
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 = ();
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Early return if this is not happening inside the impl block
|
||||
let impl_id = if let Some(impl_id) = self.resolver.impl_def() {
|
||||
impl_id
|
||||
} else {
|
||||
return;
|
||||
let mut collector = TypeAliasImplTraitCollector {
|
||||
db: self.db,
|
||||
table: &mut self.table,
|
||||
assocs: FxHashMap::default(),
|
||||
non_assocs: FxHashMap::default(),
|
||||
};
|
||||
|
||||
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 {
|
||||
for ty in tait_candidates {
|
||||
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();
|
||||
|
||||
if !atpit_coercion_table.is_empty() {
|
||||
self.table.atpit_coercion_table = Some(atpit_coercion_table);
|
||||
if !tait_coercion_table.is_empty() {
|
||||
self.table.tait_coercion_table = Some(tait_coercion_table);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -276,16 +276,16 @@ impl InferenceTable<'_> {
|
|||
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 _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 !matches!(
|
||||
from_ty.kind(Interner),
|
||||
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 = &_to;
|
||||
}
|
||||
|
|
|
@ -224,7 +224,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
|
|||
pub(crate) struct InferenceTable<'a> {
|
||||
pub(crate) db: &'a dyn HirDatabase,
|
||||
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,
|
||||
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
|
||||
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
|
||||
|
@ -244,7 +244,7 @@ impl<'a> InferenceTable<'a> {
|
|||
InferenceTable {
|
||||
db,
|
||||
trait_env,
|
||||
atpit_coercion_table: None,
|
||||
tait_coercion_table: None,
|
||||
var_unification_table: ChalkInferenceTable::new(),
|
||||
type_variable_table: SmallVec::new(),
|
||||
pending_obligations: Vec::new(),
|
||||
|
|
|
@ -391,7 +391,7 @@ pub fn layout_of_ty_query(
|
|||
let infer = db.infer(func.into());
|
||||
return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
|
||||
}
|
||||
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
|
||||
crate::ImplTraitId::TypeAliasImplTrait(..) => {
|
||||
return Err(LayoutError::NotImplemented);
|
||||
}
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
|
||||
|
|
|
@ -595,7 +595,7 @@ impl TypeFoldable<Interner> for CallableSig {
|
|||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum ImplTraitId {
|
||||
ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
|
||||
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
|
||||
TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
|
||||
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
|
||||
}
|
||||
impl InternValueTrivial for ImplTraitId {}
|
||||
|
|
|
@ -341,7 +341,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
|
||||
let impl_trait_id = origin.either(
|
||||
|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 generics =
|
||||
|
@ -2131,7 +2131,6 @@ pub(crate) fn type_alias_impl_traits(
|
|||
if let Some(type_ref) = &data.type_ref {
|
||||
let _ty = ctx.lower_ty(type_ref);
|
||||
}
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let type_alias_impl_traits = ImplTraits {
|
||||
impl_traits: match ctx.impl_trait_mode {
|
||||
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() {
|
||||
None
|
||||
} else {
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,8 +82,8 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
|
|||
};
|
||||
filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
|
||||
}
|
||||
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
|
||||
not_supported!("associated type impl trait");
|
||||
crate::ImplTraitId::TypeAliasImplTrait(..) => {
|
||||
not_supported!("type alias impl trait");
|
||||
}
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
|
||||
not_supported!("async block impl trait");
|
||||
|
|
|
@ -9,6 +9,7 @@ mod patterns;
|
|||
mod regression;
|
||||
mod simple;
|
||||
mod traits;
|
||||
mod type_alias_impl_traits;
|
||||
|
||||
use std::env;
|
||||
|
||||
|
|
|
@ -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]
|
||||
fn dyn_trait_with_lifetime_in_rpit() {
|
||||
check_types(
|
||||
|
|
161
crates/hir-ty/src/tests/type_alias_impl_traits.rs
Normal file
161
crates/hir-ty/src/tests/type_alias_impl_traits.rs
Normal 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
|
||||
"#]],
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue