mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-12 00:24:03 +00:00
Implement return position impl trait / opaque type support
This is working, but I'm not that happy with how the lowering works. We might need an additional representation between `TypeRef` and `Ty` where names are resolved and `impl Trait` bounds are separated out, but things like inference variables don't exist and `impl Trait` is always represented the same way. Also note that this doesn't implement correct handling of RPIT *inside* the function (which involves turning the `impl Trait`s into variables and creating obligations for them). That intermediate representation might help there as well.
This commit is contained in:
parent
9c52f527a1
commit
02962b374e
@ -3,8 +3,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{
|
||||
db::DefDatabase, DefWithBodyId, GenericDefId, ImplId, LocalFieldId, TraitId, TypeParamId,
|
||||
VariantId,
|
||||
db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TraitId,
|
||||
TypeParamId, VariantId,
|
||||
};
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
|
||||
@ -13,8 +13,8 @@ use ra_prof::profile;
|
||||
use crate::{
|
||||
method_resolution::{CrateImplDefs, TyFingerprint},
|
||||
traits::{chalk, AssocTyValue, Impl},
|
||||
Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty,
|
||||
TyDefId, TypeCtor, ValueTyDefId,
|
||||
Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig,
|
||||
ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
@ -48,6 +48,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||
#[salsa::invoke(crate::callable_item_sig)]
|
||||
fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig;
|
||||
|
||||
#[salsa::invoke(crate::lower::return_type_impl_traits)]
|
||||
fn return_type_impl_traits(
|
||||
&self,
|
||||
def: FunctionId,
|
||||
) -> Option<Arc<Binders<ReturnTypeImplTraits>>>;
|
||||
|
||||
#[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
|
||||
#[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
|
||||
fn generic_predicates_for_param(
|
||||
@ -80,6 +86,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||
#[salsa::interned]
|
||||
fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId;
|
||||
#[salsa::interned]
|
||||
fn intern_impl_trait_id(&self, id: OpaqueTyId) -> InternedOpaqueTyId;
|
||||
#[salsa::interned]
|
||||
fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId;
|
||||
#[salsa::interned]
|
||||
fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId;
|
||||
@ -142,3 +150,7 @@ fn hir_database_is_object_safe() {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct GlobalTypeParamId(salsa::InternId);
|
||||
impl_intern_key!(GlobalTypeParamId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct InternedOpaqueTyId(salsa::InternId);
|
||||
impl_intern_key!(InternedOpaqueTyId);
|
||||
|
@ -359,6 +359,21 @@ impl HirDisplay for ApplicationTy {
|
||||
write!(f, ">")?;
|
||||
}
|
||||
}
|
||||
TypeCtor::OpaqueType(opaque_ty_id) => {
|
||||
let bounds = match opaque_ty_id {
|
||||
crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
|
||||
let datas =
|
||||
f.db.return_type_impl_traits(func).expect("impl trait id without data");
|
||||
let data = (*datas)
|
||||
.as_ref()
|
||||
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
|
||||
data.clone().subst(&self.parameters)
|
||||
}
|
||||
};
|
||||
write!(f, "impl ")?;
|
||||
write_bounds_like_dyn_trait(&bounds.value, f)?;
|
||||
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
|
||||
}
|
||||
TypeCtor::Closure { .. } => {
|
||||
let sig = self.parameters[0].callable_sig(f.db);
|
||||
if let Some(sig) = sig {
|
||||
@ -427,14 +442,24 @@ impl HirDisplay for Ty {
|
||||
}
|
||||
}
|
||||
Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
match self {
|
||||
Ty::Dyn(_) => write!(f, "dyn ")?,
|
||||
Ty::Opaque(_) => write!(f, "impl ")?,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ty::Dyn(predicates) => {
|
||||
write!(f, "dyn ")?;
|
||||
write_bounds_like_dyn_trait(predicates, f)?;
|
||||
}
|
||||
Ty::Opaque(opaque_ty) => {
|
||||
let bounds = match opaque_ty.opaque_ty_id {
|
||||
crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
|
||||
let datas =
|
||||
f.db.return_type_impl_traits(func).expect("impl trait id without data");
|
||||
let data = (*datas)
|
||||
.as_ref()
|
||||
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
|
||||
data.clone().subst(&opaque_ty.parameters)
|
||||
}
|
||||
};
|
||||
write!(f, "impl ")?;
|
||||
write_bounds_like_dyn_trait(&bounds.value, f)?;
|
||||
}
|
||||
Ty::Unknown => write!(f, "{{unknown}}")?,
|
||||
Ty::Infer(..) => write!(f, "_")?,
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ use ra_syntax::SmolStr;
|
||||
use super::{
|
||||
primitive::{FloatTy, IntTy},
|
||||
traits::{Guidance, Obligation, ProjectionPredicate, Solution},
|
||||
ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment,
|
||||
TraitRef, Ty, TypeCtor, TypeWalk, Uncertain,
|
||||
ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
|
||||
TypeWalk, Uncertain,
|
||||
};
|
||||
use crate::{
|
||||
db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
|
||||
@ -383,25 +383,6 @@ impl<'a> InferenceContext<'a> {
|
||||
) -> Ty {
|
||||
match assoc_ty {
|
||||
Some(res_assoc_ty) => {
|
||||
// FIXME:
|
||||
// Check if inner_ty is is `impl Trait` and contained input TypeAlias id
|
||||
// this is a workaround while Chalk assoc type projection doesn't always work yet,
|
||||
// but once that is fixed I don't think we should keep this
|
||||
// (we'll probably change how associated types are resolved anyway)
|
||||
if let Ty::Opaque(ref predicates) = inner_ty {
|
||||
for p in predicates.iter() {
|
||||
if let GenericPredicate::Projection(projection) = p {
|
||||
if projection.projection_ty.associated_ty == res_assoc_ty {
|
||||
if let ty_app!(_, params) = &projection.ty {
|
||||
if params.len() == 0 {
|
||||
return projection.ty.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ty = self.table.new_type_var();
|
||||
let builder = Substs::build_for_def(self.db, res_assoc_ty)
|
||||
.push(inner_ty)
|
||||
|
@ -147,6 +147,12 @@ pub enum TypeCtor {
|
||||
/// an **application type** like `(Iterator::Item)<T>`.
|
||||
AssociatedType(TypeAliasId),
|
||||
|
||||
/// This represents a placeholder for an opaque type in situations where we
|
||||
/// don't know the hidden type (i.e. currently almost always). This is
|
||||
/// analogous to the `AssociatedType` type constructor. As with that one,
|
||||
/// these are only produced by Chalk.
|
||||
OpaqueType(OpaqueTyId),
|
||||
|
||||
/// The type of a specific closure.
|
||||
///
|
||||
/// The closure signature is stored in a `FnPtr` type in the first type
|
||||
@ -194,6 +200,14 @@ impl TypeCtor {
|
||||
let generic_params = generics(db.upcast(), type_alias.into());
|
||||
generic_params.len()
|
||||
}
|
||||
TypeCtor::OpaqueType(opaque_ty_id) => {
|
||||
match opaque_ty_id {
|
||||
OpaqueTyId::ReturnTypeImplTrait(func, _) => {
|
||||
let generic_params = generics(db.upcast(), func.into());
|
||||
generic_params.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
TypeCtor::FnPtr { num_args } => num_args as usize + 1,
|
||||
TypeCtor::Tuple { cardinality } => cardinality as usize,
|
||||
}
|
||||
@ -220,6 +234,11 @@ impl TypeCtor {
|
||||
TypeCtor::AssociatedType(type_alias) => {
|
||||
Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate)
|
||||
}
|
||||
TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
|
||||
OpaqueTyId::ReturnTypeImplTrait(func, _) => {
|
||||
Some(func.lookup(db.upcast()).module(db.upcast()).krate)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,6 +260,7 @@ impl TypeCtor {
|
||||
TypeCtor::Adt(adt) => Some(adt.into()),
|
||||
TypeCtor::FnDef(callable) => Some(callable.into()),
|
||||
TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()),
|
||||
TypeCtor::OpaqueType(_impl_trait_id) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -254,6 +274,12 @@ pub struct ApplicationTy {
|
||||
pub parameters: Substs,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct OpaqueTy {
|
||||
pub opaque_ty_id: OpaqueTyId,
|
||||
pub parameters: Substs,
|
||||
}
|
||||
|
||||
/// A "projection" type corresponds to an (unnormalized)
|
||||
/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
|
||||
/// trait and all its parameters are fully known.
|
||||
@ -308,6 +334,12 @@ pub enum Ty {
|
||||
/// trait and all its parameters are fully known.
|
||||
Projection(ProjectionTy),
|
||||
|
||||
/// An opaque type (`impl Trait`).
|
||||
///
|
||||
/// This is currently only used for return type impl trait; each instance of
|
||||
/// `impl Trait` in a return type gets its own ID.
|
||||
Opaque(OpaqueTy),
|
||||
|
||||
/// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T)
|
||||
/// {}` when we're type-checking the body of that function. In this
|
||||
/// situation, we know this stands for *some* type, but don't know the exact
|
||||
@ -332,12 +364,6 @@ pub enum Ty {
|
||||
/// didn't seem worth the overhead yet.
|
||||
Dyn(Arc<[GenericPredicate]>),
|
||||
|
||||
/// An opaque type (`impl Trait`).
|
||||
///
|
||||
/// The predicates are quantified over the `Self` type; see `Ty::Dyn` for
|
||||
/// more.
|
||||
Opaque(Arc<[GenericPredicate]>),
|
||||
|
||||
/// A placeholder for a type which could not be computed; this is propagated
|
||||
/// to avoid useless error messages. Doubles as a placeholder where type
|
||||
/// variables are inserted before type checking, since we want to try to
|
||||
@ -490,7 +516,7 @@ impl Deref for Substs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct Binders<T> {
|
||||
pub num_binders: usize,
|
||||
pub value: T,
|
||||
@ -534,6 +560,20 @@ impl<T: TypeWalk> Binders<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TypeWalk> TypeWalk for Binders<T> {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.value.walk(f);
|
||||
}
|
||||
|
||||
fn walk_mut_binders(
|
||||
&mut self,
|
||||
f: &mut impl FnMut(&mut Ty, DebruijnIndex),
|
||||
binders: DebruijnIndex,
|
||||
) {
|
||||
self.value.walk_mut_binders(f, binders.shifted_in())
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
|
||||
/// Name to be bikeshedded: TraitBound? TraitImplements?
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
@ -947,11 +987,16 @@ impl TypeWalk for Ty {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
Ty::Dyn(predicates) => {
|
||||
for p in predicates.iter() {
|
||||
p.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Opaque(o_ty) => {
|
||||
for t in o_ty.parameters.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
|
||||
}
|
||||
f(self);
|
||||
@ -969,13 +1014,48 @@ impl TypeWalk for Ty {
|
||||
Ty::Projection(p_ty) => {
|
||||
p_ty.parameters.walk_mut_binders(f, binders);
|
||||
}
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
Ty::Dyn(predicates) => {
|
||||
for p in make_mut_slice(predicates) {
|
||||
p.walk_mut_binders(f, binders.shifted_in());
|
||||
}
|
||||
}
|
||||
Ty::Opaque(o_ty) => {
|
||||
o_ty.parameters.walk_mut_binders(f, binders);
|
||||
}
|
||||
Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
|
||||
}
|
||||
f(self, binders);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TypeWalk> TypeWalk for Vec<T> {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
for t in self {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
fn walk_mut_binders(
|
||||
&mut self,
|
||||
f: &mut impl FnMut(&mut Ty, DebruijnIndex),
|
||||
binders: DebruijnIndex,
|
||||
) {
|
||||
for t in self {
|
||||
t.walk_mut_binders(f, binders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum OpaqueTyId {
|
||||
ReturnTypeImplTrait(hir_def::FunctionId, u16),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ReturnTypeImplTraits {
|
||||
pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub(crate) struct ReturnTypeImplTrait {
|
||||
pub(crate) bounds: Binders<Vec<GenericPredicate>>,
|
||||
}
|
||||
|
@ -31,8 +31,9 @@ use crate::{
|
||||
all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
|
||||
make_mut_slice, variant_data,
|
||||
},
|
||||
Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
|
||||
ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig,
|
||||
ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs,
|
||||
TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
@ -47,7 +48,16 @@ pub struct TyLoweringContext<'a> {
|
||||
/// possible currently, so this should be fine for now.
|
||||
pub type_param_mode: TypeParamLoweringMode,
|
||||
pub impl_trait_mode: ImplTraitLoweringMode,
|
||||
pub impl_trait_counter: std::cell::Cell<u16>,
|
||||
impl_trait_counter: std::cell::Cell<u16>,
|
||||
/// When turning `impl Trait` into opaque types, we have to collect the
|
||||
/// bounds at the same time to get the IDs correct (without becoming too
|
||||
/// complicated). I don't like using interior mutability (as for the
|
||||
/// counter), but I've tried and failed to make the lifetimes work for
|
||||
/// passing around a `&mut TyLoweringContext`. The core problem is that
|
||||
/// we're grouping the mutable data (the counter and this field) together
|
||||
/// with the immutable context (the references to the DB and resolver).
|
||||
/// Splitting this up would be a possible fix.
|
||||
opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>,
|
||||
}
|
||||
|
||||
impl<'a> TyLoweringContext<'a> {
|
||||
@ -56,7 +66,34 @@ impl<'a> TyLoweringContext<'a> {
|
||||
let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
|
||||
let type_param_mode = TypeParamLoweringMode::Placeholder;
|
||||
let in_binders = DebruijnIndex::INNERMOST;
|
||||
Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode }
|
||||
let opaque_type_data = std::cell::RefCell::new(Vec::new());
|
||||
Self {
|
||||
db,
|
||||
resolver,
|
||||
in_binders,
|
||||
impl_trait_mode,
|
||||
impl_trait_counter,
|
||||
type_param_mode,
|
||||
opaque_type_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_debruijn<T>(
|
||||
&self,
|
||||
debruijn: DebruijnIndex,
|
||||
f: impl FnOnce(&TyLoweringContext) -> T,
|
||||
) -> T {
|
||||
let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
|
||||
let new_ctx = Self {
|
||||
in_binders: debruijn,
|
||||
impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
|
||||
opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec),
|
||||
..*self
|
||||
};
|
||||
let result = f(&new_ctx);
|
||||
self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
|
||||
self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
|
||||
result
|
||||
}
|
||||
|
||||
pub fn with_shifted_in<T>(
|
||||
@ -64,18 +101,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
debruijn: DebruijnIndex,
|
||||
f: impl FnOnce(&TyLoweringContext) -> T,
|
||||
) -> T {
|
||||
let new_ctx = Self {
|
||||
in_binders: self.in_binders.shifted_in_from(debruijn),
|
||||
impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
|
||||
..*self
|
||||
};
|
||||
let result = f(&new_ctx);
|
||||
self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
|
||||
result
|
||||
}
|
||||
|
||||
pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self {
|
||||
Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self }
|
||||
self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f)
|
||||
}
|
||||
|
||||
pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
|
||||
@ -167,20 +193,47 @@ impl Ty {
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
match ctx.impl_trait_mode {
|
||||
ImplTraitLoweringMode::Opaque => {
|
||||
let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
|
||||
let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
|
||||
bounds
|
||||
.iter()
|
||||
.flat_map(|b| {
|
||||
GenericPredicate::from_type_bound(ctx, b, self_ty.clone())
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
Ty::Opaque(predicates)
|
||||
let idx = ctx.impl_trait_counter.get();
|
||||
ctx.impl_trait_counter.set(idx + 1);
|
||||
|
||||
assert!(idx as usize == ctx.opaque_type_data.borrow().len());
|
||||
// this dance is to make sure the data is in the right
|
||||
// place even if we encounter more opaque types while
|
||||
// lowering the bounds
|
||||
ctx.opaque_type_data
|
||||
.borrow_mut()
|
||||
.push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) });
|
||||
// We don't want to lower the bounds inside the binders
|
||||
// we're currently in, because they don't end up inside
|
||||
// those binders. E.g. when we have `impl Trait<impl
|
||||
// OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer
|
||||
// to the self parameter from `impl Trait`, and the
|
||||
// bounds aren't actually stored nested within each
|
||||
// other, but separately. So if the `T` refers to a type
|
||||
// parameter of the outer function, it's just one binder
|
||||
// away instead of two.
|
||||
let actual_opaque_type_data = ctx
|
||||
.with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
|
||||
ReturnTypeImplTrait::from_hir(ctx, &bounds)
|
||||
});
|
||||
ctx.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
|
||||
|
||||
let func = match ctx.resolver.generic_def() {
|
||||
Some(GenericDefId::FunctionId(f)) => f,
|
||||
_ => {
|
||||
// this shouldn't happen
|
||||
return (Ty::Unknown, None);
|
||||
}
|
||||
};
|
||||
let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx);
|
||||
let generics = generics(ctx.db.upcast(), func.into());
|
||||
let parameters = Substs::bound_vars(&generics, ctx.in_binders);
|
||||
Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
|
||||
}
|
||||
ImplTraitLoweringMode::Param => {
|
||||
let idx = ctx.impl_trait_counter.get();
|
||||
ctx.impl_trait_counter.set(idx + 1);
|
||||
// FIXME we're probably doing something wrong here
|
||||
ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
|
||||
if let Some(def) = ctx.resolver.generic_def() {
|
||||
let generics = generics(ctx.db.upcast(), def);
|
||||
let param = generics
|
||||
@ -197,7 +250,8 @@ impl Ty {
|
||||
}
|
||||
ImplTraitLoweringMode::Variable => {
|
||||
let idx = ctx.impl_trait_counter.get();
|
||||
ctx.impl_trait_counter.set(idx + 1);
|
||||
// FIXME we're probably doing something wrong here
|
||||
ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
|
||||
let (parent_params, self_params, list_params, _impl_trait_params) =
|
||||
if let Some(def) = ctx.resolver.generic_def() {
|
||||
let generics = generics(ctx.db.upcast(), def);
|
||||
@ -663,6 +717,29 @@ fn assoc_type_bindings_from_type_bound<'a>(
|
||||
})
|
||||
}
|
||||
|
||||
impl ReturnTypeImplTrait {
|
||||
fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self {
|
||||
let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
|
||||
let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
|
||||
bounds
|
||||
.iter()
|
||||
.flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
|
||||
.collect()
|
||||
});
|
||||
ReturnTypeImplTrait { bounds: Binders::new(1, predicates) }
|
||||
}
|
||||
}
|
||||
|
||||
fn count_impl_traits(type_ref: &TypeRef) -> usize {
|
||||
let mut count = 0;
|
||||
type_ref.walk(&mut |type_ref| {
|
||||
if matches!(type_ref, TypeRef::ImplTrait(_)) {
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
count
|
||||
}
|
||||
|
||||
/// Build the signature of a callable item (function, struct or enum variant).
|
||||
pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig {
|
||||
match def {
|
||||
@ -864,7 +941,9 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
|
||||
.with_type_param_mode(TypeParamLoweringMode::Variable);
|
||||
let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>();
|
||||
let ctx_ret = ctx_params.with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
|
||||
let ctx_ret = TyLoweringContext::new(db, &resolver)
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(TypeParamLoweringMode::Variable);
|
||||
let ret = Ty::from_hir(&ctx_ret, &data.ret_type);
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let num_binders = generics.len();
|
||||
@ -1084,3 +1163,25 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
|
||||
TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn return_type_impl_traits(
|
||||
db: &impl HirDatabase,
|
||||
def: hir_def::FunctionId,
|
||||
) -> Option<Arc<Binders<ReturnTypeImplTraits>>> {
|
||||
// FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
|
||||
let data = db.function_data(def);
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let ctx_ret = TyLoweringContext::new(db, &resolver)
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(TypeParamLoweringMode::Variable);
|
||||
let _ret = Ty::from_hir(&ctx_ret, &data.ret_type);
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let num_binders = generics.len();
|
||||
let return_type_impl_traits =
|
||||
ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
|
||||
if return_type_impl_traits.impl_traits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Arc::new(Binders::new(num_binders, return_type_impl_traits)))
|
||||
}
|
||||
}
|
||||
|
@ -1110,7 +1110,6 @@ fn test() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn impl_trait() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
@ -1160,6 +1159,52 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_pos_impl_trait() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait Iterator {
|
||||
type Item;
|
||||
fn next(&mut self) -> Self::Item;
|
||||
}
|
||||
trait Trait<T> {
|
||||
fn foo(&self) -> T;
|
||||
}
|
||||
fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} }
|
||||
fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} }
|
||||
|
||||
fn test() {
|
||||
// let (a, b) = bar();
|
||||
// a.next().foo();
|
||||
// b.foo();
|
||||
let (c, d) = baz(1u128);
|
||||
c.next();//.foo();
|
||||
// d.foo();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
50..54 'self': &mut Self
|
||||
102..106 'self': &Self
|
||||
185..196 '{ loop {} }': ({unknown}, {unknown})
|
||||
187..194 'loop {}': !
|
||||
192..194 '{}': ()
|
||||
207..208 't': T
|
||||
269..280 '{ loop {} }': ({unknown}, {unknown})
|
||||
271..278 'loop {}': !
|
||||
276..278 '{}': ()
|
||||
292..429 '{ ...o(); }': ()
|
||||
368..374 '(c, d)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
|
||||
369..370 'c': impl Iterator<Item = impl Trait<u128>>
|
||||
372..373 'd': impl Trait<u128>
|
||||
377..380 'baz': fn baz<u128>(u128) -> (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
|
||||
377..387 'baz(1u128)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
|
||||
381..386 '1u128': u128
|
||||
393..394 'c': impl Iterator<Item = impl Trait<u128>>
|
||||
393..401 'c.next()': impl Trait<u128>
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_trait() {
|
||||
assert_snapshot!(
|
||||
|
@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||
use log::debug;
|
||||
|
||||
use chalk_ir::{fold::shift::Shift, GenericArg, TypeName};
|
||||
use chalk_solve::rust_ir::{self, WellKnownTrait};
|
||||
use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
|
||||
|
||||
use hir_def::{
|
||||
lang_item::{lang_attr, LangItemTarget},
|
||||
@ -100,6 +100,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> {
|
||||
self.db.associated_ty_value(self.krate, id)
|
||||
}
|
||||
|
||||
fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> {
|
||||
vec![]
|
||||
}
|
||||
@ -130,11 +131,34 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
|
||||
}
|
||||
|
||||
fn opaque_ty_data(
|
||||
&self,
|
||||
_id: chalk_ir::OpaqueTyId<Interner>,
|
||||
) -> Arc<rust_ir::OpaqueTyDatum<Interner>> {
|
||||
unimplemented!()
|
||||
fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
|
||||
let interned_id = crate::db::InternedOpaqueTyId::from(id);
|
||||
let full_id = self.db.lookup_intern_impl_trait_id(interned_id);
|
||||
let (func, idx) = match full_id {
|
||||
crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx),
|
||||
};
|
||||
let datas =
|
||||
self.db.return_type_impl_traits(func).expect("impl trait id without impl traits");
|
||||
let data = &datas.value.impl_traits[idx as usize];
|
||||
let bound = OpaqueTyDatumBound {
|
||||
bounds: make_binders(
|
||||
data.bounds
|
||||
.value
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|b| !b.is_error())
|
||||
.map(|b| b.to_chalk(self.db))
|
||||
.collect(),
|
||||
1,
|
||||
),
|
||||
};
|
||||
let num_vars = datas.num_binders;
|
||||
Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) })
|
||||
}
|
||||
|
||||
fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
|
||||
// FIXME: actually provide the hidden type; it is relevant for auto traits
|
||||
Ty::Unknown.to_chalk(self.db)
|
||||
}
|
||||
|
||||
fn force_impl_for(
|
||||
@ -150,10 +174,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
// FIXME: implement actual object safety
|
||||
true
|
||||
}
|
||||
|
||||
fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
|
||||
Ty::Unknown.to_chalk(self.db)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn program_clauses_for_chalk_env_query(
|
||||
@ -460,6 +480,18 @@ impl From<crate::traits::GlobalImplId> for ImplId {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId {
|
||||
fn from(id: OpaqueTyId) -> Self {
|
||||
InternKey::from_intern_id(id.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId {
|
||||
fn from(id: crate::db::InternedOpaqueTyId) -> Self {
|
||||
chalk_ir::OpaqueTyId(id.as_intern_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId {
|
||||
fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self {
|
||||
Self::from_intern_id(id.0)
|
||||
|
@ -22,6 +22,8 @@ pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interne
|
||||
pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>;
|
||||
pub type FnDefId = chalk_ir::FnDefId<Interner>;
|
||||
pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
|
||||
pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
|
||||
pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
|
||||
|
||||
impl chalk_ir::interner::Interner for Interner {
|
||||
type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
|
||||
|
@ -16,8 +16,8 @@ use crate::{
|
||||
db::HirDatabase,
|
||||
primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain},
|
||||
traits::{builtin, AssocTyValue, Canonical, Impl, Obligation},
|
||||
ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy,
|
||||
Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
|
||||
ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId,
|
||||
ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
|
||||
};
|
||||
|
||||
use super::interner::*;
|
||||
@ -68,7 +68,16 @@ impl ToChalk for Ty {
|
||||
let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
|
||||
chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
|
||||
}
|
||||
Ty::Opaque(_) | Ty::Unknown => {
|
||||
Ty::Opaque(opaque_ty) => {
|
||||
let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db);
|
||||
let substitution = opaque_ty.parameters.to_chalk(db);
|
||||
chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
|
||||
opaque_ty_id,
|
||||
substitution,
|
||||
}))
|
||||
.intern(&Interner)
|
||||
}
|
||||
Ty::Unknown => {
|
||||
let substitution = chalk_ir::Substitution::empty(&Interner);
|
||||
let name = TypeName::Error;
|
||||
chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
|
||||
@ -98,7 +107,11 @@ impl ToChalk for Ty {
|
||||
let parameters = from_chalk(db, proj.substitution);
|
||||
Ty::Projection(ProjectionTy { associated_ty, parameters })
|
||||
}
|
||||
chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
|
||||
chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => {
|
||||
let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id);
|
||||
let parameters = from_chalk(db, opaque_ty.substitution);
|
||||
Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
|
||||
}
|
||||
chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => {
|
||||
let parameters: Substs = from_chalk(db, substitution);
|
||||
Ty::Apply(ApplicationTy {
|
||||
@ -204,6 +217,21 @@ impl ToChalk for hir_def::TraitId {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToChalk for OpaqueTyId {
|
||||
type Chalk = chalk_ir::OpaqueTyId<Interner>;
|
||||
|
||||
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId<Interner> {
|
||||
db.intern_impl_trait_id(self).into()
|
||||
}
|
||||
|
||||
fn from_chalk(
|
||||
db: &dyn HirDatabase,
|
||||
opaque_ty_id: chalk_ir::OpaqueTyId<Interner>,
|
||||
) -> OpaqueTyId {
|
||||
db.lookup_intern_impl_trait_id(opaque_ty_id.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToChalk for TypeCtor {
|
||||
type Chalk = TypeName<Interner>;
|
||||
|
||||
@ -214,6 +242,11 @@ impl ToChalk for TypeCtor {
|
||||
TypeName::AssociatedType(type_id)
|
||||
}
|
||||
|
||||
TypeCtor::OpaqueType(impl_trait_id) => {
|
||||
let id = impl_trait_id.to_chalk(db);
|
||||
TypeName::OpaqueType(id)
|
||||
}
|
||||
|
||||
TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
|
||||
TypeCtor::Char => TypeName::Scalar(Scalar::Char),
|
||||
TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
|
||||
@ -252,7 +285,9 @@ impl ToChalk for TypeCtor {
|
||||
match type_name {
|
||||
TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
|
||||
TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
|
||||
TypeName::OpaqueType(_) => unreachable!(),
|
||||
TypeName::OpaqueType(opaque_type_id) => {
|
||||
TypeCtor::OpaqueType(from_chalk(db, opaque_type_id))
|
||||
}
|
||||
|
||||
TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
|
||||
TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
|
||||
|
@ -69,6 +69,11 @@ impl DebugContext<'_> {
|
||||
let name = self.0.type_alias_data(type_alias).name.clone();
|
||||
write!(f, "{}::{}", trait_name, name)?;
|
||||
}
|
||||
TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
|
||||
crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
|
||||
write!(f, "{{impl trait {} of {:?}}}", idx, func)?;
|
||||
}
|
||||
},
|
||||
TypeCtor::Closure { def, expr } => {
|
||||
write!(f, "{{closure {:?} in ", expr.into_raw())?;
|
||||
match def {
|
||||
|
Loading…
Reference in New Issue
Block a user