]> git.lizzy.rs Git - rust.git/commitdiff
Implement default associated type inheritance.
authorAaron Turon <aturon@mozilla.com>
Tue, 29 Dec 2015 21:37:34 +0000 (13:37 -0800)
committerAaron Turon <aturon@mozilla.com>
Mon, 14 Mar 2016 22:04:37 +0000 (15:04 -0700)
This commit leverages the specialization graph infrastructure to allow
specializing trait implementations to leave off associated types for
which their parents have provided defaults.

It also modifies the type projection code to avoid projecting associated
types unless either (1) all input types are fully known or (2) the
available associated type is "final", i.e. not marked `default`.
This restriction is required for soundness, due to examples like:

```rust
trait Foo {
    type Assoc;
}

impl<T> Foo for T {
    default type Assoc = ();
}

impl Foo for u8 {
    type Assoc = String;
}

fn generic<T>() -> <T as Foo>::Assoc {
    () //~ ERROR
}

fn main() {
    let s: String = generic::<u8>();
    println!("{}", s); // bad news
}
```

src/librustc/middle/traits/project.rs
src/librustc_typeck/check/mod.rs

index e36307feddbf79b3a7ec68bdfcd414bbd19cf97a..dc279aae32cbb9d2decc3f2d9991c1dd9dc22d34 100644 (file)
@@ -11,6 +11,7 @@
 //! Code for projecting associated types out of trait references.
 
 use super::elaborate_predicates;
+use super::get_impl_item_or_default;
 use super::report_overflow_error;
 use super::Obligation;
 use super::ObligationCause;
@@ -23,8 +24,9 @@
 
 use middle::infer::{self, TypeOrigin};
 use middle::subst::Subst;
-use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
+use middle::ty::{self, ToPredicate, RegionEscape, HasTypeFlags, ToPolyTraitRef, Ty, TyCtxt};
 use middle::ty::fold::{TypeFoldable, TypeFolder};
+use rustc_front::hir;
 use syntax::parse::token;
 use util::common::FN_OUTPUT_NAME;
 
@@ -742,6 +744,28 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
 
     match vtable {
         super::VtableImpl(data) => {
+            if data.substs.types.needs_infer() {
+                let assoc_ty_opt = get_impl_item_or_default(selcx.tcx(), data.impl_def_id, |cand| {
+                    if let &ty::TypeTraitItem(ref assoc_ty) = cand {
+                        if assoc_ty.name == obligation.predicate.item_name {
+                            return Some(assoc_ty.defaultness);
+                        }
+                    }
+                    None
+                });
+
+                if let Some((defaultness, source)) = assoc_ty_opt {
+                    if !source.is_from_trait() && defaultness == hir::Defaultness::Default {
+                        // FIXME: is it OK to not mark as ambiguous?
+                        return Ok(());
+                    }
+                } else {
+                    selcx.tcx().sess.span_bug(obligation.cause.span,
+                                              &format!("No associated type for {:?}",
+                                                       obligation_trait_ref));
+                }
+            }
+
             debug!("assemble_candidates_from_impls: impl candidate {:?}",
                    data);
 
@@ -941,43 +965,31 @@ fn confirm_impl_candidate<'cx,'tcx>(
     impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>)
     -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
 {
-    // there don't seem to be nicer accessors to these:
-    let impl_or_trait_items_map = selcx.tcx().impl_or_trait_items.borrow();
-
-    // Look for the associated type in the impl
-    for impl_item in &selcx.tcx().impl_items.borrow()[&impl_vtable.impl_def_id] {
-        if let ty::TypeTraitItem(ref assoc_ty) = impl_or_trait_items_map[&impl_item.def_id()] {
-            if assoc_ty.name == obligation.predicate.item_name {
-                return (assoc_ty.ty.unwrap().subst(selcx.tcx(), impl_vtable.substs),
-                        impl_vtable.nested);
-            }
-        }
-    }
+    let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
 
-    // It is not in the impl - get the default from the trait.
-    let trait_ref = obligation.predicate.trait_ref;
-    for trait_item in selcx.tcx().trait_items(trait_ref.def_id).iter() {
-        if let &ty::TypeTraitItem(ref assoc_ty) = trait_item {
+    get_impl_item_or_default(selcx.tcx(), impl_def_id, |cand| {
+        if let &ty::TypeTraitItem(ref assoc_ty) = cand {
             if assoc_ty.name == obligation.predicate.item_name {
                 if let Some(ty) = assoc_ty.ty {
-                    return (ty.subst(selcx.tcx(), trait_ref.substs),
-                            impl_vtable.nested);
+                    return Some(ty)
                 } else {
-                    // This means that the impl is missing a
-                    // definition for the associated type. This error
-                    // ought to be reported by the type checker method
-                    // `check_impl_items_against_trait`, so here we
-                    // just return TyError.
+                    // This means that the impl is missing a definition for the
+                    // associated type. This error will be reported by the type
+                    // checker method `check_impl_items_against_trait`, so here
+                    // we just return TyError.
                     debug!("confirm_impl_candidate: no associated type {:?} for {:?}",
                            assoc_ty.name,
-                           trait_ref);
-                    return (selcx.tcx().types.err, vec!());
+                           obligation.predicate.trait_ref);
+                    return Some(selcx.tcx().types.err);
                 }
             }
         }
-    }
-
-    selcx.tcx().sess.span_bug(obligation.cause.span,
-                              &format!("No associated type for {:?}",
-                                       trait_ref));
+        None
+    }).map(|(ty, source)| {
+        (ty.subst(selcx.tcx(), &source.translate_substs(selcx.tcx(), substs)), nested)
+    }).unwrap_or_else(|| {
+        selcx.tcx().sess.span_bug(obligation.cause.span,
+                                  &format!("No associated type for {:?}",
+                                           obligation.predicate.trait_ref));
+    })
 }
index b6a1337dce052108f4ffe62f06bd8a09dd7e06f0..be2f63d1d1bc9305ac1461db666a3f5b783cee41 100644 (file)
@@ -1053,22 +1053,22 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                     missing_items.push(trait_method.name);
                 }
             }
-            ty::TypeTraitItem(ref associated_type) => {
-                let is_implemented = impl_items.iter().any(|ii| {
-                    match ii.node {
-                        hir::ImplItemKind::Type(_) => {
-                            ii.name == associated_type.name
+            ty::TypeTraitItem(ref trait_assoc_ty) => {
+                let search_result = traits::get_impl_item_or_default(tcx, impl_id, |cand| {
+                    if let &ty::TypeTraitItem(ref assoc_ty) = cand {
+                        if assoc_ty.name == trait_assoc_ty.name && assoc_ty.ty.is_some() {
+                            return Some(());
                         }
-                        _ => false,
                     }
+                    None
                 });
-                let is_provided = associated_type.ty.is_some();
-                if !is_implemented {
-                    if !is_provided {
-                        missing_items.push(associated_type.name);
-                    } else if associated_type_overridden {
-                        invalidated_items.push(associated_type.name);
+
+                if let Some((_, source)) = search_result {
+                    if source.is_from_trait() && associated_type_overridden {
+                        invalidated_items.push(trait_assoc_ty.name);
                     }
+                } else {
+                    missing_items.push(trait_assoc_ty.name);
                 }
             }
         }