mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
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:
commit
d3eeadc242
13 changed files with 361 additions and 55 deletions
|
@ -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
|
||||
|
|
|
@ -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!(),
|
||||
};
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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, ¶meters);
|
||||
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}}")?;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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>>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue