mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-26 06:35:27 +00:00
Implement dyn Trait unsizing as well
This commit is contained in:
parent
de39d221a1
commit
0dfbbaf03b
@ -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 {
|
||||
|
@ -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
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user