Auto merge of #16852 - ShoyuVanilla:atpit, r=Veykril

feat: Implement ATPIT

Resolves #16584

Note: This implementation only works for ATPIT, not for TAIT.
The main hinderence that blocks the later is the defining sites of TAIT can be inner blocks like in;
```rust
type X = impl Default;

mod foo {
    fn bar() -> super::X {
        ()
    }
}
```
So, to figure out we are defining it or not, we should recursively probe for nested modules and bodies.

For ATPIT, we can just look into current body because `error[E0401]: can't use 'Self' from outer item` prevent such nested structures;

```rust
trait Foo {
    type Item;
    fn foo() -> Self::Item;
}

struct Bar;

impl Foo for Bar {
    type Item = impl Default;
    fn foo() -> Self::Item {
        fn bar() -> Self::Item {
                    ^^^^^^^^^^
                    |
                    use of `Self` from outer item
                    refer to the type directly here instead
            5
        }
        bar()
    }
}
```

But this implementation does not checks for unification of same ATPIT between different bodies, monomorphization, nor layout for similar reason. (But these can be done with lazyness if we can utilize something like "mutation of interned value" with `db`. I coundn't find such thing but I would appreciate it if such thing exists and you could let me know 😅)
This commit is contained in:
bors 2024-03-18 10:38:24 +00:00
commit d3eeadc242
13 changed files with 361 additions and 55 deletions

View file

@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
};
chalk_ir::Binders::new(binders, bound)
}
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
let datas = self
.db
.type_alias_impl_traits(alias)
.expect("impl trait id without impl traits");
let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
let data = &datas.impl_traits[idx];
let bound = OpaqueTyDatumBound {
bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()),
where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
};
chalk_ir::Binders::new(binders, bound)
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
if let Some((future_trait, future_output)) = self
.db

View file

@ -268,6 +268,13 @@ impl TyExt for Ty {
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
})
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| {
let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
})
}
}
}
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
@ -280,6 +287,13 @@ impl TyExt for Ty {
data.substitute(Interner, &opaque_ty.substitution)
})
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| {
let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
data.substitute(Interner, &opaque_ty.substitution)
})
}
// It always has an parameter for Future::Output type.
ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
};

View file

@ -11,7 +11,7 @@ use base_db::{
use hir_def::{
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId,
LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId,
};
use la_arena::ArenaMap;
use smallvec::SmallVec;
@ -23,9 +23,9 @@ use crate::{
layout::{Layout, LayoutError},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError},
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution,
TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits,
InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment,
TraitRef, Ty, TyDefId, ValueTyDefId,
};
use hir_expand::name::Name;
@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
#[salsa::invoke(crate::lower::return_type_impl_traits)]
fn return_type_impl_traits(
&self,
def: FunctionId,
) -> Option<Arc<Binders<ReturnTypeImplTraits>>>;
fn return_type_impl_traits(&self, def: FunctionId) -> Option<Arc<Binders<ImplTraits>>>;
#[salsa::invoke(crate::lower::type_alias_impl_traits)]
fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option<Arc<Binders<ImplTraits>>>;
#[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
#[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]

View file

@ -1063,6 +1063,20 @@ 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) => {
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 bounds = data.substitute(Interner, &parameters);
let krate = alias.krate(db.upcast());
write_bounds_like_dyn_trait_with_prefix(
f,
"impl",
bounds.skip_binders(),
SizedByDefault::Sized { anchor: krate },
)?;
}
ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
let future_trait = db
.lang_item(body.module(db.upcast()).krate(), LangItem::Future)
@ -1228,6 +1242,20 @@ impl HirDisplay for Ty {
SizedByDefault::Sized { anchor: krate },
)?;
}
ImplTraitId::AssociatedTypeImplTrait(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 bounds = data.substitute(Interner, &opaque_ty.substitution);
let krate = alias.krate(db.upcast());
write_bounds_like_dyn_trait_with_prefix(
f,
"impl",
bounds.skip_binders(),
SizedByDefault::Sized { anchor: krate },
)?;
}
ImplTraitId::AsyncBlockTypeImplTrait(..) => {
write!(f, "{{async block}}")?;
}

View file

@ -25,8 +25,11 @@ pub(crate) mod unify;
use std::{convert::identity, iter, ops::Index};
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
Scalar, TyKind, TypeFlags, Variance,
cast::Cast,
fold::TypeFoldable,
interner::HasInterner,
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance,
};
use either::Either;
use hir_def::{
@ -53,14 +56,14 @@ use triomphe::Arc;
use crate::{
db::HirDatabase,
fold_tys,
infer::coerce::CoerceMany,
infer::{coerce::CoerceMany, unify::InferenceTable},
lower::ImplTraitLoweringMode,
static_lifetime, to_assoc_type_id,
traits::FnTrait,
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment,
TraitRef, Ty, TyBuilder, TyExt,
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution,
TraitEnvironment, Ty, TyBuilder, TyExt,
};
// This lint has a false positive here. See the link below for details.
@ -422,7 +425,7 @@ pub struct InferenceResult {
/// unresolved or missing subpatterns or subpatterns of mismatched types.
pub type_of_pat: ArenaMap<PatId, Ty>,
pub type_of_binding: ArenaMap<BindingId, Ty>,
pub type_of_rpit: ArenaMap<RpitId, Ty>,
pub type_of_rpit: ArenaMap<ImplTraitIdx, Ty>,
/// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> {
}
fn collect_const(&mut self, data: &ConstData) {
self.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.
self.insert_atpit_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
}
fn collect_static(&mut self, data: &StaticData) {
@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> {
self.write_binding_ty(self_param, ty);
}
}
let mut params_and_ret_tys = Vec::new();
for (ty, pat) in param_tys.zip(&*self.body.params) {
let ty = self.insert_type_vars(ty);
let ty = self.normalize_associated_types_in(ty);
self.infer_top_pat(*pat, &ty);
params_and_ret_tys.push(ty);
}
let return_ty = &*data.ret_type;
@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> {
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
let result =
self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
let result = self.insert_inference_vars_for_impl_trait(
return_ty,
rpits.clone(),
fn_placeholders,
);
let rpits = rpits.skip_binders();
for (id, _) in rpits.impl_traits.iter() {
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
@ -817,13 +830,19 @@ 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.
// 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());
}
fn insert_inference_vars_for_rpit<T>(
fn insert_inference_vars_for_impl_trait<T>(
&mut self,
t: T,
rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
fn_placeholders: Substitution,
rpits: Arc<chalk_ir::Binders<crate::ImplTraits>>,
placeholders: Substitution,
) -> T
where
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
@ -837,6 +856,7 @@ impl<'a> InferenceContext<'a> {
};
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx,
_ => unreachable!(),
};
let bounds =
@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> {
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 predicate = bound.map(|it| it.cloned()).substitute(Interner, &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(
let var_predicate = self.insert_inference_vars_for_impl_trait(
var_predicate,
rpits.clone(),
fn_placeholders.clone(),
placeholders.clone(),
);
self.push_obligation(var_predicate.cast(Interner));
}
@ -863,6 +882,106 @@ 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.
///
/// The inference var stretagy is effective because;
///
/// - It can still unify types that coerced into ATPIT
/// - 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> {
table: &'b mut InferenceTable<'a>,
opaque_tys: FxHashMap<OpaqueTyId, Ty>,
}
impl<'a, 'b> TypeVisitor<Interner> for OpaqueTyCollector<'a, 'b> {
type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
self
}
fn interner(&self) -> Interner {
Interner
}
fn visit_ty(
&mut self,
ty: &chalk_ir::Ty<Interner>,
outer_binder: DebruijnIndex,
) -> std::ops::ControlFlow<Self::BreakTy> {
let ty = self.table.resolve_ty_shallow(ty);
if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
self.opaque_tys.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 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);
}
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 atpits = self
.db
.type_alias_impl_traits(alias_id)
.expect("Marked as ATPIT but no impl traits!");
let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(
ty,
atpits,
alias_placeholders,
);
return Some((opaque_ty_id, ty));
}
}
None
})
.collect();
if !atpit_coercion_table.is_empty() {
self.table.atpit_coercion_table = Some(atpit_coercion_table);
}
}
fn infer_body(&mut self) {
match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr),

View file

@ -276,6 +276,23 @@ 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.
let mut to_ty = to_ty;
let _to;
if let Some(atpit_table) = &self.atpit_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) {
_to = ty.clone();
to_ty = &_to;
}
}
}
}
// Consider coercing the subtype to a DST
if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) {
return Ok(ret);

View file

@ -15,11 +15,11 @@ use crate::{
method_resolution::{self, VisibleFromModule},
to_chalk_trait_id,
utils::generics,
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
ValueTyDefId,
InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
TyKind, ValueTyDefId,
};
use super::{ExprOrPatId, InferenceContext, TraitRef};
use super::{ExprOrPatId, InferenceContext};
impl InferenceContext<'_> {
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {

View file

@ -10,6 +10,7 @@ use chalk_solve::infer::ParameterEnaVariableExt;
use either::Either;
use ena::unify::UnifyKey;
use hir_expand::name;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use triomphe::Arc;
@ -18,8 +19,9 @@ use crate::{
consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime,
to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue,
DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment,
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause,
InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar,
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
WhereClause,
};
impl InferenceContext<'_> {
@ -239,6 +241,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>>,
var_unification_table: ChalkInferenceTable,
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> {
InferenceTable {
db,
trait_env,
atpit_coercion_table: None,
var_unification_table: ChalkInferenceTable::new(),
type_variable_table: SmallVec::new(),
pending_obligations: Vec::new(),

View file

@ -389,6 +389,9 @@ 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(..) => {
return Err(LayoutError::NotImplemented);
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
return Err(LayoutError::NotImplemented)
}

View file

@ -587,24 +587,25 @@ impl TypeFoldable<Interner> for CallableSig {
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ImplTraitId {
ReturnTypeImplTrait(hir_def::FunctionId, RpitId),
ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
}
impl_intern_value_trivial!(ImplTraitId);
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ReturnTypeImplTraits {
pub(crate) impl_traits: Arena<ReturnTypeImplTrait>,
pub struct ImplTraits {
pub(crate) impl_traits: Arena<ImplTrait>,
}
has_interner!(ReturnTypeImplTraits);
has_interner!(ImplTraits);
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ReturnTypeImplTrait {
pub struct ImplTrait {
pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
}
pub type RpitId = Idx<ReturnTypeImplTrait>;
pub type ImplTraitIdx = Idx<ImplTrait>;
pub fn static_lifetime() -> Lifetime {
LifetimeData::Static.intern(Interner)

View file

@ -61,9 +61,9 @@ use crate::{
InTypeConstIdMetadata,
},
AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, ParamKind,
PolyFnSig, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution,
TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
};
#[derive(Debug)]
@ -76,7 +76,7 @@ enum ImplTraitLoweringState {
/// 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(RefCell<Arena<ReturnTypeImplTrait>>),
Opaque(RefCell<Arena<ImplTrait>>),
Param(Cell<u16>),
Variable(Cell<u16>),
Disallowed,
@ -301,15 +301,18 @@ impl<'a> TyLoweringContext<'a> {
TypeRef::ImplTrait(bounds) => {
match &self.impl_trait_mode {
ImplTraitLoweringState::Opaque(opaque_type_data) => {
let func = match self.resolver.generic_def() {
Some(GenericDefId::FunctionId(f)) => f,
_ => panic!("opaque impl trait lowering in non-function"),
let origin = match self.resolver.generic_def() {
Some(GenericDefId::FunctionId(it)) => Either::Left(it),
Some(GenericDefId::TypeAliasId(it)) => Either::Right(it),
_ => panic!(
"opaque impl trait lowering must be in function or type alias"
),
};
// this dance is to make sure the data is in the right
// place even if we encounter more opaque types while
// lowering the bounds
let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait {
let idx = opaque_type_data.borrow_mut().alloc(ImplTrait {
bounds: crate::make_single_type_binders(Vec::new()),
});
// We don't want to lower the bounds inside the binders
@ -323,13 +326,17 @@ impl<'a> TyLoweringContext<'a> {
// away instead of two.
let actual_opaque_type_data = self
.with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
ctx.lower_impl_trait(bounds, func)
ctx.lower_impl_trait(bounds, self.resolver.krate())
});
opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data;
let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx);
let impl_trait_id = origin.either(
|f| ImplTraitId::ReturnTypeImplTrait(f, idx),
|a| ImplTraitId::AssociatedTypeImplTrait(a, idx),
);
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
let generics = generics(self.db.upcast(), func.into());
let generics =
generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into()));
let parameters = generics.bound_vars_subst(self.db, self.in_binders);
TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner)
}
@ -1274,11 +1281,7 @@ impl<'a> TyLoweringContext<'a> {
}
}
fn lower_impl_trait(
&self,
bounds: &[Interned<TypeBound>],
func: FunctionId,
) -> ReturnTypeImplTrait {
fn lower_impl_trait(&self, bounds: &[Interned<TypeBound>], krate: CrateId) -> ImplTrait {
cov_mark::hit!(lower_rpit);
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
@ -1288,7 +1291,6 @@ impl<'a> TyLoweringContext<'a> {
.collect();
if !ctx.unsized_types.borrow().contains(&self_ty) {
let krate = func.krate(ctx.db.upcast());
let sized_trait = ctx
.db
.lang_item(krate, LangItem::Sized)
@ -1305,7 +1307,7 @@ impl<'a> TyLoweringContext<'a> {
}
predicates
});
ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) }
ImplTrait { bounds: crate::make_single_type_binders(predicates) }
}
}
@ -1873,6 +1875,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
let generics = generics(db.upcast(), t.into());
let resolver = t.resolver(db.upcast());
let ctx = TyLoweringContext::new(db, &resolver, t.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let type_alias_data = db.type_alias_data(t);
if type_alias_data.is_extern {
@ -2033,7 +2036,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
pub(crate) fn return_type_impl_traits(
db: &dyn HirDatabase,
def: hir_def::FunctionId,
) -> Option<Arc<Binders<ReturnTypeImplTraits>>> {
) -> Option<Arc<Binders<ImplTraits>>> {
// 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());
@ -2042,7 +2045,7 @@ pub(crate) fn return_type_impl_traits(
.with_type_param_mode(ParamLoweringMode::Variable);
let _ret = ctx_ret.lower_ty(&data.ret_type);
let generics = generics(db.upcast(), def.into());
let return_type_impl_traits = ReturnTypeImplTraits {
let return_type_impl_traits = ImplTraits {
impl_traits: match ctx_ret.impl_trait_mode {
ImplTraitLoweringState::Opaque(x) => x.into_inner(),
_ => unreachable!(),
@ -2055,6 +2058,32 @@ pub(crate) fn return_type_impl_traits(
}
}
pub(crate) fn type_alias_impl_traits(
db: &dyn HirDatabase,
def: hir_def::TypeAliasId,
) -> Option<Arc<Binders<ImplTraits>>> {
let data = db.type_alias_data(def);
let resolver = def.resolver(db.upcast());
let ctx = TyLoweringContext::new(db, &resolver, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
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(),
_ => unreachable!(),
},
};
if type_alias_impl_traits.impl_traits.is_empty() {
None
} else {
Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits)))
}
}
pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability {
match m {
hir_def::type_ref::Mutability::Shared => Mutability::Not,

View file

@ -82,6 +82,9 @@ 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::AsyncBlockTypeImplTrait(_, _) => {
not_supported!("async block impl trait");
}

View file

@ -4689,3 +4689,78 @@ 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>>
}
"#,
);
}