feat: Implement TAIT

This commit is contained in:
Shoyu Vanilla 2024-08-10 14:55:46 +09:00
parent 56f63dfd8a
commit c530e21714
14 changed files with 291 additions and 191 deletions

View file

@ -160,7 +160,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
#[salsa::invoke(StaticData::static_data_query)]
fn static_data(&self, konst: StaticId) -> Arc<StaticData>;
fn static_data(&self, statik: StaticId) -> Arc<StaticData>;
#[salsa::invoke(Macro2Data::macro2_data_query)]
fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;

View file

@ -275,7 +275,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
};
chalk_ir::Binders::new(binders, bound)
}
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas = self
.db
.type_alias_impl_traits(alias)

View file

@ -276,7 +276,7 @@ impl TyExt for Ty {
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
})
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| {
let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
@ -295,7 +295,7 @@ impl TyExt for Ty {
data.substitute(Interner, &opaque_ty.substitution)
})
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
db.type_alias_impl_traits(alias).map(|it| {
let data =
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());

View file

@ -1151,11 +1151,10 @@ impl HirDisplay for Ty {
)?;
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas =
db.type_alias_impl_traits(alias).expect("impl trait id without data");
let data =
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
let data = (*datas).as_ref().map(|it| it.impl_traits[idx].bounds.clone());
let bounds = data.substitute(Interner, &parameters);
let krate = alias.krate(db.upcast());
write_bounds_like_dyn_trait_with_prefix(
@ -1338,7 +1337,7 @@ impl HirDisplay for Ty {
SizedByDefault::Sized { anchor: krate },
)?;
}
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
ImplTraitId::TypeAliasImplTrait(alias, idx) => {
let datas =
db.type_alias_impl_traits(alias).expect("impl trait id without data");
let data =

View file

@ -36,15 +36,14 @@ use hir_def::{
body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
hir::LabelId,
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::{LifetimeRef, TypeRef},
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId,
TupleFieldId, TupleId, TypeAliasId, VariantId,
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup,
TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
};
use hir_expand::name::Name;
use indexmap::IndexSet;
@ -785,14 +784,19 @@ impl<'a> InferenceContext<'a> {
fn collect_const(&mut self, data: &ConstData) {
let return_ty = self.make_ty(&data.type_ref);
// Constants might be associated items that define ATPITs.
self.insert_atpit_coercion_table(iter::once(&return_ty));
// Constants might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
}
fn collect_static(&mut self, data: &StaticData) {
self.return_ty = self.make_ty(&data.type_ref);
let return_ty = self.make_ty(&data.type_ref);
// Statics might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
self.return_ty = return_ty;
}
fn collect_fn(&mut self, func: FunctionId) {
@ -857,11 +861,11 @@ impl<'a> InferenceContext<'a> {
self.return_ty = self.normalize_associated_types_in(return_ty);
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
// Functions might be associated items that define ATPITs.
// To define an ATPITs, that ATPIT must appear in the function's signatures.
// Functions might be defining usage sites of TAITs.
// To define an TAITs, that TAIT must appear in the function's signatures.
// So, it suffices to check for params and return types.
params_and_ret_tys.push(self.return_ty.clone());
self.insert_atpit_coercion_table(params_and_ret_tys.iter());
self.make_tait_coercion_table(params_and_ret_tys.iter());
}
fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
@ -880,7 +884,7 @@ impl<'a> InferenceContext<'a> {
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
(self.db.return_type_impl_traits(def), idx)
}
ImplTraitId::AssociatedTypeImplTrait(def, idx) => {
ImplTraitId::TypeAliasImplTrait(def, idx) => {
(self.db.type_alias_impl_traits(def), idx)
}
_ => unreachable!(),
@ -909,23 +913,25 @@ impl<'a> InferenceContext<'a> {
}
/// The coercion of a non-inference var into an opaque type should fail,
/// but not in the defining sites of the ATPITs.
/// In such cases, we insert an proxy inference var for each ATPIT,
/// and coerce into it instead of ATPIT itself.
/// but not in the defining sites of the TAITs.
/// In such cases, we insert an proxy inference var for each TAIT,
/// and coerce into it instead of TAIT itself.
///
/// The inference var stretagy is effective because;
///
/// - It can still unify types that coerced into ATPIT
/// - It can still unify types that coerced into TAITs
/// - We are pushing `impl Trait` bounds into it
///
/// This function inserts a map that maps the opaque type to that proxy inference var.
fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator<Item = &'b Ty>) {
struct OpaqueTyCollector<'a, 'b> {
fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator<Item = &'b Ty>) {
struct TypeAliasImplTraitCollector<'a, 'b> {
db: &'b dyn HirDatabase,
table: &'b mut InferenceTable<'a>,
opaque_tys: FxHashMap<OpaqueTyId, Ty>,
assocs: FxHashMap<OpaqueTyId, (ImplId, Ty)>,
non_assocs: FxHashMap<OpaqueTyId, Ty>,
}
impl<'a, 'b> TypeVisitor<Interner> for OpaqueTyCollector<'a, 'b> {
impl<'a, 'b> TypeVisitor<Interner> for TypeAliasImplTraitCollector<'a, 'b> {
type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
@ -944,59 +950,105 @@ impl<'a> InferenceContext<'a> {
let ty = self.table.resolve_ty_shallow(ty);
if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
self.opaque_tys.insert(*id, ty.clone());
if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id((*id).into())
{
let loc = self.db.lookup_intern_type_alias(alias_id);
match loc.container {
ItemContainerId::ImplId(impl_id) => {
self.assocs.insert(*id, (impl_id, ty.clone()));
}
ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => {
self.non_assocs.insert(*id, ty.clone());
}
_ => {}
}
}
}
ty.super_visit_with(self, outer_binder)
}
}
// Early return if this is not happening inside the impl block
let impl_id = if let Some(impl_id) = self.resolver.impl_def() {
impl_id
} else {
return;
let mut collector = TypeAliasImplTraitCollector {
db: self.db,
table: &mut self.table,
assocs: FxHashMap::default(),
non_assocs: FxHashMap::default(),
};
let assoc_tys: FxHashSet<_> = self
.db
.impl_data(impl_id)
.items
.iter()
.filter_map(|item| match item {
AssocItemId::TypeAliasId(alias) => Some(*alias),
_ => None,
})
.collect();
if assoc_tys.is_empty() {
return;
}
let mut collector =
OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() };
for ty in tys {
for ty in tait_candidates {
ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
}
let atpit_coercion_table: FxHashMap<_, _> = collector
.opaque_tys
.into_iter()
.filter_map(|(opaque_ty_id, ty)| {
if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
{
if assoc_tys.contains(&alias_id) {
let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(ty, alias_placeholders);
return Some((opaque_ty_id, ty));
}
}
None
// Non-assoc TAITs can be define-used everywhere as long as they are
// in function signatures or const types, etc
let mut taits = collector.non_assocs;
// assoc TAITs(ATPITs) can be only define-used inside their impl block.
// They cannot be define-used in inner items like in the following;
//
// ```
// impl Trait for Struct {
// type Assoc = impl Default;
//
// fn assoc_fn() -> Self::Assoc {
// let foo: Self::Assoc = true; // Allowed here
//
// fn inner() -> Self::Assoc {
// false // Not allowed here
// }
//
// foo
// }
// }
// ```
let impl_id = match self.owner {
DefWithBodyId::FunctionId(it) => {
let loc = self.db.lookup_intern_function(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
DefWithBodyId::ConstId(it) => {
let loc = self.db.lookup_intern_const(it);
if let ItemContainerId::ImplId(impl_id) = loc.container {
Some(impl_id)
} else {
None
}
}
_ => None,
};
if let Some(impl_id) = impl_id {
taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| {
if impl_ == impl_id {
Some((id, ty))
} else {
None
}
}));
}
let tait_coercion_table: FxHashMap<_, _> = taits
.into_iter()
.filter_map(|(id, ty)| {
if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id(id.into())
{
let subst = TyBuilder::placeholder_subst(self.db, alias_id);
let ty = self.insert_inference_vars_for_impl_trait(ty, subst);
Some((id, ty))
} else {
None
}
})
.collect();
if !atpit_coercion_table.is_empty() {
self.table.atpit_coercion_table = Some(atpit_coercion_table);
if !tait_coercion_table.is_empty() {
self.table.tait_coercion_table = Some(tait_coercion_table);
}
}

View file

@ -276,16 +276,16 @@ impl InferenceTable<'_> {
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
}
// If we are coercing into an ATPIT, coerce into its proxy inference var, instead.
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.
let mut to_ty = to_ty;
let _to;
if let Some(atpit_table) = &self.atpit_coercion_table {
if let Some(tait_table) = &self.tait_coercion_table {
if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) {
if !matches!(
from_ty.kind(Interner),
TyKind::InferenceVar(..) | TyKind::OpaqueType(..)
) {
if let Some(ty) = atpit_table.get(opaque_ty_id) {
if let Some(ty) = tait_table.get(opaque_ty_id) {
_to = ty.clone();
to_ty = &_to;
}

View file

@ -224,7 +224,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) trait_env: Arc<TraitEnvironment>,
pub(crate) atpit_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
pub(crate) tait_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
var_unification_table: ChalkInferenceTable,
type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
@ -244,7 +244,7 @@ impl<'a> InferenceTable<'a> {
InferenceTable {
db,
trait_env,
atpit_coercion_table: None,
tait_coercion_table: None,
var_unification_table: ChalkInferenceTable::new(),
type_variable_table: SmallVec::new(),
pending_obligations: Vec::new(),

View file

@ -391,7 +391,7 @@ pub fn layout_of_ty_query(
let infer = db.infer(func.into());
return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
}
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
crate::ImplTraitId::TypeAliasImplTrait(..) => {
return Err(LayoutError::NotImplemented);
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {

View file

@ -595,7 +595,7 @@ impl TypeFoldable<Interner> for CallableSig {
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ImplTraitId {
ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
}
impl InternValueTrivial for ImplTraitId {}

View file

@ -341,7 +341,7 @@ impl<'a> TyLoweringContext<'a> {
let impl_trait_id = origin.either(
|f| ImplTraitId::ReturnTypeImplTrait(f, idx),
|a| ImplTraitId::AssociatedTypeImplTrait(a, idx),
|a| ImplTraitId::TypeAliasImplTrait(a, idx),
);
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
let generics =
@ -2131,7 +2131,6 @@ pub(crate) fn type_alias_impl_traits(
if let Some(type_ref) = &data.type_ref {
let _ty = ctx.lower_ty(type_ref);
}
let generics = generics(db.upcast(), def.into());
let type_alias_impl_traits = ImplTraits {
impl_traits: match ctx.impl_trait_mode {
ImplTraitLoweringState::Opaque(x) => x.into_inner(),
@ -2141,6 +2140,7 @@ pub(crate) fn type_alias_impl_traits(
if type_alias_impl_traits.impl_traits.is_empty() {
None
} else {
let generics = generics(db.upcast(), def.into());
Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits)))
}
}

View file

@ -82,8 +82,8 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
};
filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
}
crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
not_supported!("associated type impl trait");
crate::ImplTraitId::TypeAliasImplTrait(..) => {
not_supported!("type alias impl trait");
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
not_supported!("async block impl trait");

View file

@ -9,6 +9,7 @@ mod patterns;
mod regression;
mod simple;
mod traits;
mod type_alias_impl_traits;
use std::env;

View file

@ -4691,119 +4691,6 @@ fn f<T: Send, U>() {
);
}
#[test]
fn associated_type_impl_trait() {
check_types(
r#"
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Self::Item;
}
struct S2;
impl Bar for S2 {
type Item = impl Foo;
fn bar(&self) -> Self::Item {
S1
}
}
fn test() {
let x = S2.bar();
//^ impl Foo + ?Sized
}
"#,
);
}
#[test]
fn associated_type_impl_traits_complex() {
check_types(
r#"
struct Unary<T>(T);
struct Binary<T, U>(T, U);
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Unary<Self::Item>;
}
struct S2;
impl Bar for S2 {
type Item = Unary<impl Foo>;
fn bar(&self) -> Unary<<Self as Bar>::Item> {
Unary(Unary(S1))
}
}
trait Baz {
type Target1;
type Target2;
fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
}
struct S3;
impl Baz for S3 {
type Target1 = impl Foo;
type Target2 = Unary<impl Bar>;
fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
Binary(S1, Unary(S2))
}
}
fn test() {
let x = S3.baz();
//^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
let y = x.1.0.bar();
//^ Unary<Bar::Item<impl Bar + ?Sized>>
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_nested_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn dyn_trait_with_lifetime_in_rpit() {
check_types(

View file

@ -0,0 +1,161 @@
use expect_test::expect;
use super::{check_infer_with_mismatches, check_no_mismatches, check_types};
#[test]
fn associated_type_impl_trait() {
check_types(
r#"
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Self::Item;
}
struct S2;
impl Bar for S2 {
type Item = impl Foo;
fn bar(&self) -> Self::Item {
S1
}
}
fn test() {
let x = S2.bar();
//^ impl Foo + ?Sized
}
"#,
);
}
#[test]
fn associated_type_impl_traits_complex() {
check_types(
r#"
struct Unary<T>(T);
struct Binary<T, U>(T, U);
trait Foo {}
struct S1;
impl Foo for S1 {}
trait Bar {
type Item;
fn bar(&self) -> Unary<Self::Item>;
}
struct S2;
impl Bar for S2 {
type Item = Unary<impl Foo>;
fn bar(&self) -> Unary<<Self as Bar>::Item> {
Unary(Unary(S1))
}
}
trait Baz {
type Target1;
type Target2;
fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
}
struct S3;
impl Baz for S3 {
type Target1 = impl Foo;
type Target2 = Unary<impl Bar>;
fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
Binary(S1, Unary(S2))
}
}
fn test() {
let x = S3.baz();
//^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
let y = x.1.0.bar();
//^ Unary<Bar::Item<impl Bar + ?Sized>>
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn associated_type_with_impl_trait_in_nested_tuple() {
check_no_mismatches(
r#"
pub trait Iterator {
type Item;
}
pub trait Value {}
fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
fn foo() {
bar();
}
"#,
);
}
#[test]
fn type_alias_impl_trait_simple() {
check_no_mismatches(
r#"
trait Trait {}
struct Struct;
impl Trait for Struct {}
type AliasTy = impl Trait;
static ALIAS: AliasTy = {
let res: AliasTy = Struct;
res
};
"#,
);
check_infer_with_mismatches(
r#"
trait Trait {}
struct Struct;
impl Trait for Struct {}
type AliasTy = impl Trait;
static ALIAS: i32 = {
// TATIs cannot be define-used if not in signature or type annotations
let _a: AliasTy = Struct;
5
};
"#,
expect![[r#"
106..220 '{ ... 5 }': i32
191..193 '_a': impl Trait + ?Sized
205..211 'Struct': Struct
217..218 '5': i32
205..211: expected impl Trait + ?Sized, got Struct
"#]],
)
}