1
Fork 0

Implement default associated type inheritance.

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
}
```
This commit is contained in:
Aaron Turon 2015-12-29 13:37:34 -08:00
parent 5dedbdaea4
commit b7e5112e88
2 changed files with 55 additions and 43 deletions

View file

@ -11,6 +11,7 @@
//! Code for projecting associated types out of trait references. //! Code for projecting associated types out of trait references.
use super::elaborate_predicates; use super::elaborate_predicates;
use super::get_impl_item_or_default;
use super::report_overflow_error; use super::report_overflow_error;
use super::Obligation; use super::Obligation;
use super::ObligationCause; use super::ObligationCause;
@ -23,8 +24,9 @@ use super::util;
use middle::infer::{self, TypeOrigin}; use middle::infer::{self, TypeOrigin};
use middle::subst::Subst; 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 middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_front::hir;
use syntax::parse::token; use syntax::parse::token;
use util::common::FN_OUTPUT_NAME; use util::common::FN_OUTPUT_NAME;
@ -742,6 +744,28 @@ fn assemble_candidates_from_impls<'cx,'tcx>(
match vtable { match vtable {
super::VtableImpl(data) => { 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 {:?}", debug!("assemble_candidates_from_impls: impl candidate {:?}",
data); data);
@ -941,43 +965,31 @@ fn confirm_impl_candidate<'cx,'tcx>(
impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>) impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>)
-> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>) -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
{ {
// there don't seem to be nicer accessors to these: let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
let impl_or_trait_items_map = selcx.tcx().impl_or_trait_items.borrow();
// Look for the associated type in the impl get_impl_item_or_default(selcx.tcx(), impl_def_id, |cand| {
for impl_item in &selcx.tcx().impl_items.borrow()[&impl_vtable.impl_def_id] { if let &ty::TypeTraitItem(ref assoc_ty) = cand {
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);
}
}
}
// 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 {
if assoc_ty.name == obligation.predicate.item_name { if assoc_ty.name == obligation.predicate.item_name {
if let Some(ty) = assoc_ty.ty { if let Some(ty) = assoc_ty.ty {
return (ty.subst(selcx.tcx(), trait_ref.substs), return Some(ty)
impl_vtable.nested);
} else { } else {
// This means that the impl is missing a // This means that the impl is missing a definition for the
// definition for the associated type. This error // associated type. This error will be reported by the type
// ought to be reported by the type checker method // checker method `check_impl_items_against_trait`, so here
// `check_impl_items_against_trait`, so here we // we just return TyError.
// just return TyError.
debug!("confirm_impl_candidate: no associated type {:?} for {:?}", debug!("confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.name, assoc_ty.name,
trait_ref); obligation.predicate.trait_ref);
return (selcx.tcx().types.err, vec!()); return Some(selcx.tcx().types.err);
} }
} }
} }
} None
}).map(|(ty, source)| {
selcx.tcx().sess.span_bug(obligation.cause.span, (ty.subst(selcx.tcx(), &source.translate_substs(selcx.tcx(), substs)), nested)
&format!("No associated type for {:?}", }).unwrap_or_else(|| {
trait_ref)); selcx.tcx().sess.span_bug(obligation.cause.span,
&format!("No associated type for {:?}",
obligation.predicate.trait_ref));
})
} }

View file

@ -1053,22 +1053,22 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
missing_items.push(trait_method.name); missing_items.push(trait_method.name);
} }
} }
ty::TypeTraitItem(ref associated_type) => { ty::TypeTraitItem(ref trait_assoc_ty) => {
let is_implemented = impl_items.iter().any(|ii| { let search_result = traits::get_impl_item_or_default(tcx, impl_id, |cand| {
match ii.node { if let &ty::TypeTraitItem(ref assoc_ty) = cand {
hir::ImplItemKind::Type(_) => { if assoc_ty.name == trait_assoc_ty.name && assoc_ty.ty.is_some() {
ii.name == associated_type.name return Some(());
} }
_ => false,
} }
None
}); });
let is_provided = associated_type.ty.is_some();
if !is_implemented { if let Some((_, source)) = search_result {
if !is_provided { if source.is_from_trait() && associated_type_overridden {
missing_items.push(associated_type.name); invalidated_items.push(trait_assoc_ty.name);
} else if associated_type_overridden {
invalidated_items.push(associated_type.name);
} }
} else {
missing_items.push(trait_assoc_ty.name);
} }
} }
} }