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:
parent
5dedbdaea4
commit
b7e5112e88
2 changed files with 55 additions and 43 deletions
|
@ -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 super::util;
|
|||
|
||||
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();
|
||||
let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {:?}",
|
||||
trait_ref));
|
||||
obligation.predicate.trait_ref));
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue