mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
Implement dyn Trait unsizing as well
This commit is contained in:
parent
de39d221a1
commit
0dfbbaf03b
5 changed files with 136 additions and 9 deletions
|
@ -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> {
|
fn builtin_deref(&self) -> Option<Ty> {
|
||||||
match self {
|
match self {
|
||||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||||
|
|
|
@ -576,7 +576,6 @@ fn test() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ignore]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coerce_unsize_trait_object() {
|
fn coerce_unsize_trait_object() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
@ -600,6 +599,13 @@ fn test() {
|
||||||
}
|
}
|
||||||
"#, true),
|
"#, true),
|
||||||
@r###"
|
@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,
|
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
|
/// An impl. Usually this comes from an impl block, but some built-in types get
|
||||||
/// synthetic impls.
|
/// synthetic impls.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -345,6 +351,10 @@ pub enum Impl {
|
||||||
ClosureFnTraitImpl(ClosureFnTraitImplData),
|
ClosureFnTraitImpl(ClosureFnTraitImplData),
|
||||||
/// [T; n]: Unsize<[T]>
|
/// [T; n]: Unsize<[T]>
|
||||||
UnsizeArray,
|
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.
|
/// This exists just for Chalk, because our ImplIds are only unique per module.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[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 hir_expand::name::name;
|
||||||
use ra_db::CrateId;
|
use ra_db::CrateId;
|
||||||
|
|
||||||
use super::{AssocTyValue, Impl};
|
use super::{AssocTyValue, Impl, UnsizeToSuperTraitObjectData};
|
||||||
use crate::{db::HirDatabase, utils::generics, ApplicationTy, Substs, TraitRef, Ty, TypeCtor};
|
use crate::{
|
||||||
|
db::HirDatabase,
|
||||||
|
utils::{all_super_traits, generics},
|
||||||
|
ApplicationTy, GenericPredicate, Substs, TraitRef, Ty, TypeCtor,
|
||||||
|
};
|
||||||
|
|
||||||
pub(super) struct BuiltinImplData {
|
pub(super) struct BuiltinImplData {
|
||||||
pub num_vars: usize,
|
pub num_vars: usize,
|
||||||
|
@ -25,6 +29,8 @@ pub(super) fn get_builtin_impls(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
ty: &Ty,
|
ty: &Ty,
|
||||||
|
// The first argument for the trait, if present
|
||||||
|
arg: &Option<Ty>,
|
||||||
trait_: TraitId,
|
trait_: TraitId,
|
||||||
mut callback: impl FnMut(Impl),
|
mut callback: impl FnMut(Impl),
|
||||||
) {
|
) {
|
||||||
|
@ -43,14 +49,43 @@ pub(super) fn get_builtin_impls(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, .. }) = ty {
|
|
||||||
if let Some(actual_trait) = get_unsize_trait(db, krate) {
|
let unsize_trait = get_unsize_trait(db, krate);
|
||||||
|
if let Some(actual_trait) = unsize_trait {
|
||||||
if trait_ == actual_trait {
|
if trait_ == actual_trait {
|
||||||
if check_unsize_impl_prerequisites(db, krate) {
|
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 {
|
||||||
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::ImplBlock(_) => unreachable!(),
|
||||||
Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
|
Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
|
||||||
Impl::UnsizeArray => array_unsize_impl_datum(db, krate),
|
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(
|
fn get_fn_trait(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
|
|
|
@ -572,8 +572,10 @@ where
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone());
|
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))
|
result.push(i.to_chalk(self.db))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue