]> git.lizzy.rs Git - rust.git/commitdiff
Implement dyn Trait unsizing as well
authorFlorian Diebold <florian.diebold@freiheit.com>
Fri, 21 Feb 2020 18:05:27 +0000 (19:05 +0100)
committerFlorian Diebold <flodiebold@gmail.com>
Sat, 22 Feb 2020 10:09:21 +0000 (11:09 +0100)
crates/ra_hir_ty/src/lib.rs
crates/ra_hir_ty/src/tests/coercion.rs
crates/ra_hir_ty/src/traits.rs
crates/ra_hir_ty/src/traits/builtin.rs
crates/ra_hir_ty/src/traits/chalk.rs

index 13c5e6c6b73fe443a8f31bedc7487ce09381a1b7..15356ab37d39aaa64ac05a937434ddbeed59dbe5 100644 (file)
@@ -661,6 +661,17 @@ pub fn as_callable(&self) -> Option<(CallableDef, &Substs)> {
         }
     }
 
+    /// 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 {
index aa2dfb5f0c173599b73a7272323a30b677d748b2..b6fce937725abd36c82161b921305448c664c667 100644 (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
     "###
     );
 }
index c385f00986d6aad86a58fff5a18247b0a2ef8c7a..2317fcac369940444420c6bde2d2ee3a683b583a 100644 (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)]
index 394232fd94112b93bb65f28ff3565bb9bf1813d7..3c8dd4af867c3587dfcb3503a74d0ab0951de6fe 100644 (file)
@@ -4,8 +4,12 @@
 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,
index 1bdf13e480d7423b9d2e2f6b4d1d6c4f985b2e16..e1e430aeb09f1578a3dfe39f26e1e75d3c53ce90 100644 (file)
@@ -572,8 +572,10 @@ fn impls_for_trait(
             .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))
         });