Infer types of nested RPITs

This commit is contained in:
hkalbasi 2023-04-11 04:32:11 +03:30
parent 44cf8ef49a
commit a584cb998f
5 changed files with 105 additions and 34 deletions

View File

@ -92,6 +92,7 @@ pub enum LayoutError {
SizeOverflow, SizeOverflow,
TargetLayoutNotAvailable, TargetLayoutNotAvailable,
HasPlaceholder, HasPlaceholder,
HasErrorType,
NotImplemented, NotImplemented,
Unknown, Unknown,
} }

View File

@ -33,7 +33,7 @@ use hir_def::{
TraitId, TypeAliasId, VariantId, TraitId, TypeAliasId, VariantId,
}; };
use hir_expand::name::{name, Name}; use hir_expand::name::{name, Name};
use la_arena::ArenaMap; use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use stdx::{always, never}; use stdx::{always, never};
@ -676,36 +676,16 @@ impl<'a> InferenceContext<'a> {
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function. // RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
fold_tys( let result =
return_ty, self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
|ty, _| { let rpits = rpits.skip_binders();
let opaque_ty_id = match ty.kind(Interner) { for (id, _) in rpits.impl_traits.iter() {
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
_ => return ty, never!("Missed RPIT in `insert_inference_vars_for_rpit`");
}; e.insert(TyKind::Error.intern(Interner));
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { }
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, }
_ => unreachable!(), result
};
let bounds = (*rpits).map_ref(|rpits| {
rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())
});
let var = self.table.new_type_var();
let var_subst = Substitution::from1(Interner, var.clone());
for bound in bounds {
let predicate =
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
let (var_predicate, binders) = predicate
.substitute(Interner, &var_subst)
.into_value_and_skipped_binders();
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
self.push_obligation(var_predicate.cast(Interner));
}
self.result.type_of_rpit.insert(idx, var.clone());
var
},
DebruijnIndex::INNERMOST,
)
} else { } else {
return_ty return_ty
}; };
@ -714,6 +694,50 @@ impl<'a> InferenceContext<'a> {
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
} }
fn insert_inference_vars_for_rpit<T>(
&mut self,
t: T,
rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
fn_placeholders: Substitution,
) -> T
where
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
{
fold_tys(
t,
|ty, _| {
let opaque_ty_id = match ty.kind(Interner) {
TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
_ => return ty,
};
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
_ => unreachable!(),
};
let bounds = (*rpits)
.map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter()));
let var = self.table.new_type_var();
let var_subst = Substitution::from1(Interner, var.clone());
for bound in bounds {
let predicate =
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
let (var_predicate, binders) =
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
let var_predicate = self.insert_inference_vars_for_rpit(
var_predicate,
rpits.clone(),
fn_placeholders.clone(),
);
self.push_obligation(var_predicate.cast(Interner));
}
self.result.type_of_rpit.insert(idx, var.clone());
var
},
DebruijnIndex::INNERMOST,
)
}
fn infer_body(&mut self) { fn infer_body(&mut self) {
match self.return_coercion { match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr), Some(_) => self.infer_return(self.body.body_expr),

View File

@ -245,8 +245,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => { TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
return Err(LayoutError::NotImplemented) return Err(LayoutError::NotImplemented)
} }
TyKind::Error => return Err(LayoutError::HasErrorType),
TyKind::AssociatedType(_, _) TyKind::AssociatedType(_, _)
| TyKind::Error
| TyKind::Alias(_) | TyKind::Alias(_)
| TyKind::Placeholder(_) | TyKind::Placeholder(_)
| TyKind::BoundVar(_) | TyKind::BoundVar(_)

View File

@ -232,6 +232,45 @@ fn return_position_impl_trait() {
fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) } fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) }
foo() foo()
} }
size_and_align_expr! {
minicore: iterators;
stmts: []
trait Tr {}
impl Tr for i32 {}
fn foo() -> impl Iterator<Item = impl Tr> {
[1, 2, 3].into_iter()
}
let mut iter = foo();
let item = iter.next();
(iter, item)
}
size_and_align_expr! {
minicore: future;
stmts: []
use core::{future::Future, task::{Poll, Context}, pin::pin};
use std::{task::Wake, sync::Arc};
trait Tr {}
impl Tr for i32 {}
async fn f() -> impl Tr {
2
}
fn unwrap_fut<T>(inp: impl Future<Output = T>) -> Poll<T> {
// In a normal test we could use `loop {}` or `panic!()` here,
// but rustc actually runs this code.
let pinned = pin!(inp);
struct EmptyWaker;
impl Wake for EmptyWaker {
fn wake(self: Arc<Self>) {
}
}
let waker = Arc::new(EmptyWaker).into();
let mut context = Context::from_waker(&waker);
let x = pinned.poll(&mut context);
x
}
let x = unwrap_fut(f());
x
}
size_and_align_expr! { size_and_align_expr! {
struct Foo<T>(T, T, (T, T)); struct Foo<T>(T, T, (T, T));
trait T {} trait T {}

View File

@ -891,12 +891,19 @@ pub mod iter {
self self
} }
} }
pub struct IntoIter<T, const N: usize>([T; N]); struct IndexRange {
start: usize,
end: usize,
}
pub struct IntoIter<T, const N: usize> {
data: [T; N],
range: IndexRange,
}
impl<T, const N: usize> IntoIterator for [T; N] { impl<T, const N: usize> IntoIterator for [T; N] {
type Item = T; type Item = T;
type IntoIter = IntoIter<T, N>; type IntoIter = IntoIter<T, N>;
fn into_iter(self) -> I { fn into_iter(self) -> I {
IntoIter(self) IntoIter { data: self, range: IndexRange { start: 0, end: self.len() } }
} }
} }
impl<T, const N: usize> Iterator for IntoIter<T, N> { impl<T, const N: usize> Iterator for IntoIter<T, N> {