Implement dyn Trait unsizing as well

This commit is contained in:
Florian Diebold 2020-02-21 19:05:27 +01:00 committed by Florian Diebold
parent de39d221a1
commit 0dfbbaf03b
5 changed files with 136 additions and 9 deletions

View File

@ -661,6 +661,17 @@ impl Ty {
}
}
/// If this is a `dyn Trait` type, this returns the `Trait` part.
pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
match self {
Ty::Dyn(bounds) => bounds.get(0).and_then(|b| match b {
GenericPredicate::Implemented(trait_ref) => Some(trait_ref),
_ => None,
}),
_ => None,
}
}
fn builtin_deref(&self) -> Option<Ty> {
match self {
Ty::Apply(a_ty) => match a_ty.ctor {

View File

@ -576,7 +576,6 @@ fn test() {
);
}
#[ignore]
#[test]
fn coerce_unsize_trait_object() {
assert_snapshot!(
@ -600,6 +599,13 @@ fn test() {
}
"#, true),
@r###"
[240; 300) '{ ...obj; }': ()
[250; 253) 'obj': &dyn Bar
[266; 268) '&S': &S
[267; 268) 'S': S
[278; 281) 'obj': &dyn Foo
[294; 297) 'obj': &dyn Bar
[294; 297): expected &dyn Foo, got &dyn Bar
"###
);
}

View File

@ -335,6 +335,12 @@ pub struct ClosureFnTraitImplData {
fn_trait: FnTrait,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnsizeToSuperTraitObjectData {
trait_: TraitId,
super_trait: TraitId,
}
/// An impl. Usually this comes from an impl block, but some built-in types get
/// synthetic impls.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -345,6 +351,10 @@ pub enum Impl {
ClosureFnTraitImpl(ClosureFnTraitImplData),
/// [T; n]: Unsize<[T]>
UnsizeArray,
/// T: Unsize<dyn Trait> where T: Trait
UnsizeToTraitObject(TraitId),
/// dyn Trait: Unsize<dyn SuperTrait> if Trait: SuperTrait
UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData),
}
/// This exists just for Chalk, because our ImplIds are only unique per module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

View File

@ -4,8 +4,12 @@ use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId};
use hir_expand::name::name;
use ra_db::CrateId;
use super::{AssocTyValue, Impl};
use crate::{db::HirDatabase, utils::generics, ApplicationTy, Substs, TraitRef, Ty, TypeCtor};
use super::{AssocTyValue, Impl, UnsizeToSuperTraitObjectData};
use crate::{
db::HirDatabase,
utils::{all_super_traits, generics},
ApplicationTy, GenericPredicate, Substs, TraitRef, Ty, TypeCtor,
};
pub(super) struct BuiltinImplData {
pub num_vars: usize,
@ -25,6 +29,8 @@ pub(super) fn get_builtin_impls(
db: &impl HirDatabase,
krate: CrateId,
ty: &Ty,
// The first argument for the trait, if present
arg: &Option<Ty>,
trait_: TraitId,
mut callback: impl FnMut(Impl),
) {
@ -43,14 +49,43 @@ pub(super) fn get_builtin_impls(
}
}
}
let unsize_trait = get_unsize_trait(db, krate);
if let Some(actual_trait) = unsize_trait {
if trait_ == actual_trait {
get_builtin_unsize_impls(db, krate, ty, arg, callback);
}
}
}
fn get_builtin_unsize_impls(
db: &impl HirDatabase,
krate: CrateId,
ty: &Ty,
// The first argument for the trait, if present
arg: &Option<Ty>,
mut callback: impl FnMut(Impl),
) {
if !check_unsize_impl_prerequisites(db, krate) {
return;
}
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, .. }) = ty {
if let Some(actual_trait) = get_unsize_trait(db, krate) {
if trait_ == actual_trait {
if check_unsize_impl_prerequisites(db, krate) {
callback(Impl::UnsizeArray);
}
callback(Impl::UnsizeArray);
}
if let Some(target_trait) = arg.as_ref().and_then(|t| t.dyn_trait_ref()) {
if let Some(trait_ref) = ty.dyn_trait_ref() {
let super_traits = all_super_traits(db, trait_ref.trait_);
if super_traits.contains(&target_trait.trait_) {
// callback(Impl::UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData {
// trait_: trait_ref.trait_,
// super_trait: target_trait.trait_,
// }));
}
}
callback(Impl::UnsizeToTraitObject(target_trait.trait_));
}
}
@ -59,6 +94,10 @@ pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) ->
Impl::ImplBlock(_) => unreachable!(),
Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
Impl::UnsizeArray => array_unsize_impl_datum(db, krate),
Impl::UnsizeToTraitObject(trait_) => trait_object_unsize_impl_datum(db, krate, trait_),
Impl::UnsizeToSuperTraitObject(data) => {
super_trait_object_unsize_impl_datum(db, krate, data)
}
}
}
@ -216,6 +255,65 @@ fn array_unsize_impl_datum(db: &impl HirDatabase, krate: CrateId) -> BuiltinImpl
}
}
// Trait object unsizing
fn trait_object_unsize_impl_datum(
db: &impl HirDatabase,
krate: CrateId,
trait_: TraitId,
) -> BuiltinImplData {
// impl<T, T1, ...> Unsize<dyn Trait<T1, ...>> for T where T: Trait<T1, ...>
let unsize_trait = get_unsize_trait(db, krate) // get unsize trait
// the existence of the Unsize trait has been checked before
.expect("Unsize trait missing");
let self_ty = Ty::Bound(0);
let substs = Substs::build_for_def(db, trait_)
// this fits together nicely: $0 is our self type, and the rest are the type
// args for the trait
.fill_with_bound_vars(0)
.build();
let trait_ref = TraitRef { trait_, substs };
// This is both the bound for the `dyn` type, *and* the bound for the impl!
// This works because the self type for `dyn` is always Ty::Bound(0), which
// we've also made the parameter for our impl self type.
let bounds = vec![GenericPredicate::Implemented(trait_ref)];
let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(bounds.clone().into())).build();
let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs };
BuiltinImplData { num_vars: 1, trait_ref, where_clauses: bounds, assoc_ty_values: Vec::new() }
}
fn super_trait_object_unsize_impl_datum(
db: &impl HirDatabase,
krate: CrateId,
_data: UnsizeToSuperTraitObjectData,
) -> BuiltinImplData {
// impl Unsize<dyn SuperTrait> for dyn Trait
let unsize_trait = get_unsize_trait(db, krate) // get unsize trait
// the existence of the Unsize trait has been checked before
.expect("Unsize trait missing");
let substs = Substs::builder(2)
// .push(Ty::Dyn(todo!()))
// .push(Ty::Dyn(todo!()))
.build();
let trait_ref = TraitRef { trait_: unsize_trait, substs };
BuiltinImplData {
num_vars: 1,
trait_ref,
where_clauses: Vec::new(),
assoc_ty_values: Vec::new(),
}
}
fn get_fn_trait(
db: &impl HirDatabase,
krate: CrateId,

View File

@ -572,8 +572,10 @@ where
.collect();
let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone());
let arg: Option<Ty> =
parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref().clone()));
builtin::get_builtin_impls(self.db, self.krate, &ty, trait_, |i| {
builtin::get_builtin_impls(self.db, self.krate, &ty, &arg, trait_, |i| {
result.push(i.to_chalk(self.db))
});