diff --git a/src/librustc/dep_graph/README.md b/src/librustc/dep_graph/README.md index 21742d9935dc2..ece5819829baa 100644 --- a/src/librustc/dep_graph/README.md +++ b/src/librustc/dep_graph/README.md @@ -51,7 +51,7 @@ could invalidate work done for other items. So, for example: not shared state, because if it changes it does not itself invalidate other functions (though it may be that it causes new monomorphizations to occur, but that's handled independently). - + Put another way: if the HIR for an item changes, we are going to recompile that item for sure. But we need the dep tracking map to tell us what *else* we have to recompile. Shared state is anything that is @@ -177,7 +177,7 @@ reads from `item`, there would be missing edges in the graph: | ^ | | +---------------------------------+ // added by `visit_all_items_in_krate` - + In particular, the edge from `Hir(X)` to `ItemSignature(X)` is only present because we called `read` ourselves when entering the `ItemSignature(X)` task. @@ -273,8 +273,8 @@ should not exist. In contrast, using the memoized helper, you get: ... -> MapVariant(key) -> A | +----------> B - -which is much cleaner. + +which is much cleaner. **Be aware though that the closure is executed with `MapVariant(key)` pushed onto the stack as the current task!** That means that you must @@ -387,4 +387,3 @@ RUST_DEP_GRAPH_FILTER='Hir&foo -> TypeckItemBody & bar' This will dump out all the nodes that lead from `Hir(foo)` to `TypeckItemBody(bar)`, from which you can (hopefully) see the source of the erroneous edge. - diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 51985be96db53..89b57e0d90a00 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -25,6 +25,7 @@ use middle::expr_use_visitor as euv; use middle::infer; use middle::mem_categorization::{cmt}; use middle::pat_util::*; +use middle::traits::ProjectionMode; use middle::ty::*; use middle::ty; use std::cmp::Ordering; @@ -1101,7 +1102,8 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, //FIXME: (@jroesch) this code should be floated up as well let infcx = infer::new_infer_ctxt(cx.tcx, &cx.tcx.tables, - Some(cx.param_env.clone())); + Some(cx.param_env.clone()), + ProjectionMode::AnyFinal); if infcx.type_moves_by_default(pat_ty, pat.span) { check_move(p, sub.as_ref().map(|p| &**p)); } @@ -1133,7 +1135,8 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>, let infcx = infer::new_infer_ctxt(cx.tcx, &cx.tcx.tables, - Some(checker.cx.param_env.clone())); + Some(checker.cx.param_env.clone()), + ProjectionMode::AnyFinal); let mut visitor = ExprUseVisitor::new(&mut checker, &infcx); visitor.walk_expr(guard); diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index d97df585edc05..8a1a0080eb0c2 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -24,6 +24,7 @@ use middle::def_id::DefId; use middle::pat_util::def_to_path; use middle::ty::{self, Ty, TyCtxt}; use middle::ty::util::IntTypeExt; +use middle::traits::ProjectionMode; use middle::astconv_util::ast_ty_to_prim_ty; use util::nodemap::NodeMap; @@ -1049,7 +1050,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, trait_ref); tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id()); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); let mut selcx = traits::SelectionContext::new(&infcx); let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), @@ -1067,6 +1068,11 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, } }; + // NOTE: this code does not currently account for specialization, but when + // it does so, it should hook into the ProjectionMode to determine when the + // constant should resolve; this will also require plumbing through to this + // function whether we are in "trans mode" to pick the right ProjectionMode + // when constructing the inference context above. match selection { traits::VtableImpl(ref impl_data) => { match tcx.associated_consts(impl_data.impl_def_id) diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 193492ee7e10c..6a4680ecbaf31 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -176,6 +176,7 @@ pub trait CrateStore<'tcx> : Any { -> Option; fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId) -> Vec>>; + fn impl_parent(&self, impl_def_id: DefId) -> Option; // trait/impl-item info fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) @@ -346,6 +347,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { { unimplemented!() } fn associated_consts(&self, tcx: &TyCtxt<'tcx>, def: DefId) -> Vec>> { unimplemented!() } + fn impl_parent(&self, def: DefId) -> Option { unimplemented!() } // trait/impl-item info fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index e671dd73431e6..4ff1de422117b 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -27,7 +27,7 @@ use middle::region::CodeExtent; use middle::subst; use middle::subst::Substs; use middle::subst::Subst; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use middle::ty::adjustment; use middle::ty::{TyVid, IntVid, FloatVid}; use middle::ty::{self, Ty, TyCtxt}; @@ -99,6 +99,11 @@ pub struct InferCtxt<'a, 'tcx: 'a> { // directly. normalize: bool, + // Sadly, the behavior of projection varies a bit depending on the + // stage of compilation. The specifics are given in the + // documentation for `ProjectionMode`. + projection_mode: ProjectionMode, + err_count_on_creation: usize, } @@ -354,7 +359,8 @@ pub fn fixup_err_to_string(f: FixupError) -> String { pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, tables: &'a RefCell>, - param_env: Option>) + param_env: Option>, + projection_mode: ProjectionMode) -> InferCtxt<'a, 'tcx> { InferCtxt { tcx: tcx, @@ -366,14 +372,16 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()), reported_trait_errors: RefCell::new(FnvHashSet()), normalize: false, + projection_mode: projection_mode, err_count_on_creation: tcx.sess.err_count() } } pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a TyCtxt<'tcx>, - tables: &'a RefCell>) + tables: &'a RefCell>, + projection_mode: ProjectionMode) -> InferCtxt<'a, 'tcx> { - let mut infcx = new_infer_ctxt(tcx, tables, None); + let mut infcx = new_infer_ctxt(tcx, tables, None, projection_mode); infcx.normalize = true; infcx } @@ -514,6 +522,7 @@ pub struct CombinedSnapshot { region_vars_snapshot: RegionSnapshot, } +// NOTE: Callable from trans only! pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T where T : TypeFoldable<'tcx> { @@ -525,7 +534,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &TyCtxt<'tcx>, value: &T) -> T return value; } - let infcx = new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Any); let mut selcx = traits::SelectionContext::new(&infcx); let cause = traits::ObligationCause::dummy(); let traits::Normalized { value: result, obligations } = @@ -593,6 +602,10 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + pub fn projection_mode(&self) -> ProjectionMode { + self.projection_mode + } + pub fn freshen>(&self, t: T) -> T { t.fold_with(&mut self.freshener()) } @@ -1025,8 +1038,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { substs: &mut Substs<'tcx>, defs: &[ty::TypeParameterDef<'tcx>]) { - let mut vars = Vec::with_capacity(defs.len()); - for def in defs.iter() { let default = def.default.map(|default| { type_variable::Default { @@ -1038,7 +1049,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty_var = self.next_ty_var_with_default(default); substs.types.push(space, ty_var); - vars.push(ty_var) } } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 02dfeb80b928f..510a3dd454b95 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -160,6 +160,15 @@ impl<'tcx> Substs<'tcx> { Substs { types: types, regions: regions } } + pub fn with_method_from_subst(self, other: &Substs<'tcx>) -> Substs<'tcx> { + let Substs { types, regions } = self; + let types = types.with_slice(FnSpace, other.types.get_slice(FnSpace)); + let regions = regions.map(|r| { + r.with_slice(FnSpace, other.regions().get_slice(FnSpace)) + }); + Substs { types: types, regions: regions } + } + /// Creates a trait-ref out of this substs, ignoring the FnSpace substs pub fn to_trait_ref(&self, tcx: &TyCtxt<'tcx>, trait_id: DefId) -> ty::TraitRef<'tcx> { diff --git a/src/librustc/middle/traits/README.md b/src/librustc/middle/traits/README.md index 92982af92dcfe..ff72f9dd07e36 100644 --- a/src/librustc/middle/traits/README.md +++ b/src/librustc/middle/traits/README.md @@ -428,3 +428,43 @@ We used to try and draw finer-grained distinctions, but that led to a serious of annoying and weird bugs like #22019 and #18290. This simple rule seems to be pretty clearly safe and also still retains a very high hit rate (~95% when compiling rustc). + +# Specialization + +Defined in the `specialize` module. + +The basic strategy is to build up a *specialization graph* during +coherence checking. Insertion into the graph locates the right place +to put an impl in the specialization hierarchy; if there is no right +place (due to partial overlap but no containment), you get an overlap +error. Specialization is consulted when selecting an impl (of course), +and the graph is consulted when propagating defaults down the +specialization hierarchy. + +You might expect that the specialization graph would be used during +selection -- i.e., when actually performing specialization. This is +not done for two reasons: + +- It's merely an optimization: given a set of candidates that apply, + we can determine the most specialized one by comparing them directly + for specialization, rather than consulting the graph. Given that we + also cache the results of selection, the benefit of this + optimization is questionable. + +- To build the specialization graph in the first place, we need to use + selection (because we need to determine whether one impl specializes + another). Dealing with this reentrancy would require some additional + mode switch for selection. Given that there seems to be no strong + reason to use the graph anyway, we stick with a simpler approach in + selection, and use the graph only for propagating default + implementations. + +Trait impl selection can succeed even when multiple impls can apply, +as long as they are part of the same specialization family. In that +case, it returns a *single* impl on success -- this is the most +specialized impl *known* to apply. However, if there are any inference +variables in play, the returned impl may not be the actual impl we +will use at trans time. Thus, we take special care to avoid projecting +associated types unless either (1) the associated type does not use +`default` and thus cannot be overridden or (2) all input types are +known concretely. diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 6005d36ff4eb2..64d1c992cbf1d 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -10,8 +10,7 @@ //! See `README.md` for high-level documentation -use super::{SelectionContext}; -use super::{Obligation, ObligationCause}; +use super::{SelectionContext, Obligation, ObligationCause}; use middle::cstore::LOCAL_CRATE; use middle::def_id::DefId; @@ -23,8 +22,8 @@ use syntax::codemap::DUMMY_SP; #[derive(Copy, Clone)] struct InferIsLocal(bool); -/// If there are types that satisfy both impls, returns an `ImplTy` -/// with those types substituted (by updating the given `infcx`) +/// If there are types that satisfy both impls, returns a suitably-freshened +/// `ImplHeader` with those types substituted pub fn overlapping_impls<'cx, 'tcx>(infcx: &InferCtxt<'cx, 'tcx>, impl1_def_id: DefId, impl2_def_id: DefId) diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 8a2f0c0c09304..5f66e9e6344ad 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -36,20 +36,18 @@ pub use self::coherence::orphan_check; pub use self::coherence::overlapping_impls; pub use self::coherence::OrphanCheckErr; pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation}; -pub use self::project::MismatchedProjectionTypes; -pub use self::project::normalize; -pub use self::project::Normalized; +pub use self::project::{MismatchedProjectionTypes, ProjectionMode}; +pub use self::project::{normalize, Normalized}; pub use self::object_safety::is_object_safe; pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::object_safety_violations; pub use self::object_safety::ObjectSafetyViolation; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::is_vtable_safe_method; -pub use self::select::EvaluationCache; -pub use self::select::SelectionContext; -pub use self::select::SelectionCache; +pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchedData}; // intentionally don't export variants +pub use self::specialize::{Overlap, specialization_graph, specializes, translate_substs}; pub use self::util::elaborate_predicates; pub use self::util::get_vtable_index_of_object_method; pub use self::util::trait_ref_for_builtin_bound; @@ -67,6 +65,7 @@ mod fulfill; mod project; mod object_safety; mod select; +mod specialize; mod structural_impls; mod util; @@ -434,7 +433,10 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi let elaborated_env = unnormalized_env.with_caller_bounds(predicates); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env)); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(elaborated_env), + ProjectionMode::AnyFinal); let predicates = match fully_normalize(&infcx, cause, &infcx.parameter_environment.caller_bounds) { diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index e36307feddbf7..e86f3ed01a49d 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -12,6 +12,8 @@ use super::elaborate_predicates; use super::report_overflow_error; +use super::specialization_graph; +use super::translate_substs; use super::Obligation; use super::ObligationCause; use super::PredicateObligation; @@ -21,13 +23,103 @@ use super::VtableClosureData; use super::VtableImplData; use super::util; +use middle::def_id::DefId; use middle::infer::{self, TypeOrigin}; use middle::subst::Subst; use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt}; use middle::ty::fold::{TypeFoldable, TypeFolder}; use syntax::parse::token; +use syntax::ast; use util::common::FN_OUTPUT_NAME; +use std::rc::Rc; + +/// Depending on the stage of compilation, we want projection to be +/// more or less conservative. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ProjectionMode { + /// FIXME (#32205) + /// At coherence-checking time, we're still constructing the + /// specialization graph, and thus we only project project + /// non-`default` associated types that are defined directly in + /// the applicable impl. (This behavior should be improved over + /// time, to allow for successful projections modulo cycles + /// between different impls). + /// + /// Here's an example that will fail due to the restriction: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// type Output = bool; + /// } + /// + /// impl Assoc for u8 {} // <- inherits the non-default type from above + /// + /// trait Foo {} + /// impl Foo for u32 {} + /// impl Foo for ::Output {} // <- this projection will fail + /// ``` + /// + /// The projection would succeed if `Output` had been defined + /// directly in the impl for `u8`. + Topmost, + + /// At type-checking time, we refuse to project any associated + /// type that is marked `default`. Non-`default` ("final") types + /// are always projected. This is necessary in general for + /// soundness of specialization. However, we *could* allow + /// projections in fully-monomorphic cases. We choose not to, + /// because we prefer for `default type` to force the type + /// definition to be treated abstractly by any consumers of the + /// impl. Concretely, that means that the following example will + /// fail to compile: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// default type Output = bool; + /// } + /// + /// fn main() { + /// let <() as Assoc>::Output = true; + /// } + AnyFinal, + + /// At trans time, all projections will succeed. + Any, +} + +impl ProjectionMode { + pub fn is_topmost(&self) -> bool { + match *self { + ProjectionMode::Topmost => true, + _ => false, + } + } + + pub fn is_any_final(&self) -> bool { + match *self { + ProjectionMode::AnyFinal => true, + _ => false, + } + } + + pub fn is_any(&self) -> bool { + match *self { + ProjectionMode::Any => true, + _ => false, + } + } +} + + pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -566,7 +658,76 @@ fn project_type<'cx,'tcx>( assert!(candidates.vec.len() <= 1); - match candidates.vec.pop() { + let possible_candidate = candidates.vec.pop().and_then(|candidate| { + // In Any (i.e. trans) mode, all projections succeed; + // otherwise, we need to be sensitive to `default` and + // specialization. + if !selcx.projection_mode().is_any() { + if let ProjectionTyCandidate::Impl(ref impl_data) = candidate { + if let Some(node_item) = assoc_ty_def(selcx, + impl_data.impl_def_id, + obligation.predicate.item_name) { + if node_item.node.is_from_trait() { + if node_item.item.ty.is_some() { + // If the associated type has a default from the + // trait, that should be considered `default` and + // hence not projected. + // + // Note, however, that we allow a projection from + // the trait specifically in the case that the trait + // does *not* give a default. This is purely to + // avoid spurious errors: the situation can only + // arise when *no* impl in the specialization chain + // has provided a definition for the type. When we + // confirm the candidate, we'll turn the projection + // into a TyError, since the actual error will be + // reported in `check_impl_items_against_trait`. + return None; + } + } else if node_item.item.defaultness.is_default() { + return None; + } + } else { + // Normally this situation could only arise througha + // compiler bug, but at coherence-checking time we only look + // at the topmost impl (we don't even consider the trait + // itself) for the definition -- so we can fail to find a + // definition of the type even if it exists. + + // For now, we just unconditionally ICE, because otherwise, + // examples like the following will succeed: + // + // ``` + // trait Assoc { + // type Output; + // } + // + // impl Assoc for T { + // default type Output = bool; + // } + // + // impl Assoc for u8 {} + // impl Assoc for u16 {} + // + // trait Foo {} + // impl Foo for ::Output {} + // impl Foo for ::Output {} + // return None; + // } + // ``` + // + // The essential problem here is that the projection fails, + // leaving two unnormalized types, which appear not to unify + // -- so the overlap check succeeds, when it should fail. + selcx.tcx().sess.bug("Tried to project an inherited associated type during \ + coherence checking, which is currently not supported."); + } + } + } + Some(candidate) + }); + + match possible_candidate { Some(candidate) => { let (ty, obligations) = confirm_candidate(selcx, obligation, candidate); Ok(ProjectedTy::Progress(ty, obligations)) @@ -941,43 +1102,63 @@ fn confirm_impl_candidate<'cx,'tcx>( impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>) -> (Ty<'tcx>, Vec>) { - // 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; + + let tcx = selcx.tcx(); + let trait_ref = obligation.predicate.trait_ref; + let assoc_ty = assoc_ty_def(selcx, impl_def_id, obligation.predicate.item_name); + + match assoc_ty { + Some(node_item) => { + let ty = node_item.item.ty.unwrap_or_else(|| { + // 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 {:?}", + node_item.item.name, + obligation.predicate.trait_ref); + tcx.types.err + }); + let substs = translate_substs(selcx.infcx(), impl_def_id, substs, node_item.node); + (ty.subst(tcx, substs), nested) + } + None => { + tcx.sess.span_bug(obligation.cause.span, + &format!("No associated type for {:?}", trait_ref)); } } +} - // 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 let Some(ty) = assoc_ty.ty { - return (ty.subst(selcx.tcx(), trait_ref.substs), - impl_vtable.nested); - } 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. - debug!("confirm_impl_candidate: no associated type {:?} for {:?}", - assoc_ty.name, - trait_ref); - return (selcx.tcx().types.err, vec!()); +/// Locate the definition of an associated type in the specialization hierarchy, +/// starting from the given impl. +/// +/// Based on the "projection mode", this lookup may in fact only examine the +/// topmost impl. See the comments for `ProjectionMode` for more details. +fn assoc_ty_def<'cx, 'tcx>(selcx: &SelectionContext<'cx, 'tcx>, + impl_def_id: DefId, + assoc_ty_name: ast::Name) + -> Option>>> +{ + let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id; + + if selcx.projection_mode().is_topmost() { + let impl_node = specialization_graph::Node::Impl(impl_def_id); + for item in impl_node.items(selcx.tcx()) { + if let ty::TypeTraitItem(assoc_ty) = item { + if assoc_ty.name == assoc_ty_name { + return Some(specialization_graph::NodeItem { + node: specialization_graph::Node::Impl(impl_def_id), + item: assoc_ty, + }); } } } + None + } else { + selcx.tcx().lookup_trait_def(trait_def_id) + .ancestors(impl_def_id) + .type_defs(selcx.tcx(), assoc_ty_name) + .next() } - - selcx.tcx().sess.span_bug(obligation.cause.span, - &format!("No associated type for {:?}", - trait_ref)); } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index fbfd4b67b5bd5..3ef163f225c4a 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -25,6 +25,7 @@ use super::report_overflow_error; use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation}; use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch}; use super::{ObjectCastObligation, Obligation}; +use super::ProjectionMode; use super::TraitNotObjectSafe; use super::Selection; use super::SelectionResult; @@ -40,6 +41,7 @@ use middle::infer; use middle::infer::{InferCtxt, TypeFreshener, TypeOrigin}; use middle::subst::{Subst, Substs, TypeSpace}; use middle::ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; +use middle::traits; use middle::ty::fast_reject; use middle::ty::relate::TypeRelation; @@ -75,7 +77,6 @@ pub struct SelectionContext<'cx, 'tcx:'cx> { /// other words, we consider `$0 : Bar` to be unimplemented if /// there is no type that the user could *actually name* that /// would satisfy it. This avoids crippling inference, basically. - intercrate: bool, } @@ -224,6 +225,12 @@ struct SelectionCandidateSet<'tcx> { ambiguous: bool, } +#[derive(PartialEq,Eq,Debug,Clone)] +struct EvaluatedCandidate<'tcx> { + candidate: SelectionCandidate<'tcx>, + evaluation: EvaluationResult, +} + enum BuiltinBoundConditions<'tcx> { If(ty::Binder>>), ParameterBuiltin, @@ -251,8 +258,7 @@ pub struct EvaluationCache<'tcx> { } impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) - -> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx: infcx, freshener: infcx.freshener(), @@ -260,8 +266,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) - -> SelectionContext<'cx, 'tcx> { + pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx: infcx, freshener: infcx.freshener(), @@ -285,6 +290,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx } + pub fn projection_mode(&self) -> ProjectionMode { + self.infcx.projection_mode() + } + /////////////////////////////////////////////////////////////////////////// // Selection // @@ -558,7 +567,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // this crate, perhaps the type would be unified with // something from another crate that does provide an impl. // - // In intracrate mode, we must still be conservative. The reason is + // In intra mode, we must still be conservative. The reason is // that we want to avoid cycles. Imagine an impl like: // // impl Eq for Vec @@ -746,6 +755,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidate } + // Treat negative impls as unimplemented + fn filter_negative_impls(&self, candidate: SelectionCandidate<'tcx>) + -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let ImplCandidate(def_id) = candidate { + if self.tcx().trait_impl_polarity(def_id) == Some(hir::ImplPolarity::Negative) { + return Err(Unimplemented) + } + } + Ok(Some(candidate)) + } + fn candidate_from_obligation_no_cache<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> @@ -762,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if !self.is_knowable(stack) { - debug!("intercrate not knowable"); + debug!("coherence stage: not knowable"); return Ok(None); } @@ -803,12 +823,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // we were to winnow, we'd wind up with zero candidates. // Instead, we select the right impl now but report `Bar does // not implement Clone`. - if candidates.len() > 1 { - candidates.retain(|c| self.evaluate_candidate(stack, c).may_apply()) - } + if candidates.len() == 1 { + return self.filter_negative_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. + let mut candidates: Vec<_> = candidates.into_iter().filter_map(|c| { + let eval = self.evaluate_candidate(stack, &c); + if eval.may_apply() { + Some(EvaluatedCandidate { + candidate: c, + evaluation: eval, + }) + } else { + None + } + }).collect(); - // If there are STILL multiple candidate, we can further reduce - // the list by dropping duplicates. + // If there are STILL multiple candidate, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. if candidates.len() > 1 { let mut i = 0; while i < candidates.len() { @@ -836,8 +871,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(None); } - - // If there are *NO* candidates, that there are no impls -- + // If there are *NO* candidates, then there are no impls -- // that we know of, anyway. Note that in the case where there // are unbound type variables within the obligation, it might // be the case that you could still satisfy the obligation @@ -851,19 +885,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // Just one candidate left. - let candidate = candidates.pop().unwrap(); - - match candidate { - ImplCandidate(def_id) => { - match self.tcx().trait_impl_polarity(def_id) { - Some(hir::ImplPolarity::Negative) => return Err(Unimplemented), - _ => {} - } - } - _ => {} - } - - Ok(Some(candidate)) + self.filter_negative_impls(candidates.pop().unwrap().candidate) } fn is_knowable<'o>(&mut self, @@ -1565,41 +1587,54 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// candidates and prefer where-clause candidates. /// /// See the comment for "SelectionCandidate" for more details. - fn candidate_should_be_dropped_in_favor_of<'o>(&mut self, - victim: &SelectionCandidate<'tcx>, - other: &SelectionCandidate<'tcx>) - -> bool + fn candidate_should_be_dropped_in_favor_of<'o>( + &mut self, + victim: &EvaluatedCandidate<'tcx>, + other: &EvaluatedCandidate<'tcx>) + -> bool { - if victim == other { + if victim.candidate == other.candidate { return true; } - match other { - &ObjectCandidate | - &ParamCandidate(_) | &ProjectionCandidate => match victim { - &DefaultImplCandidate(..) => { + match other.candidate { + ObjectCandidate | + ParamCandidate(_) | ProjectionCandidate => match victim.candidate { + DefaultImplCandidate(..) => { self.tcx().sess.bug( "default implementations shouldn't be recorded \ when there are other valid candidates"); } - &ImplCandidate(..) | - &ClosureCandidate(..) | - &FnPointerCandidate | - &BuiltinObjectCandidate | - &BuiltinUnsizeCandidate | - &DefaultImplObjectCandidate(..) | - &BuiltinCandidate(..) => { + ImplCandidate(..) | + ClosureCandidate(..) | + FnPointerCandidate | + BuiltinObjectCandidate | + BuiltinUnsizeCandidate | + DefaultImplObjectCandidate(..) | + BuiltinCandidate(..) => { // We have a where-clause so don't go around looking // for impls. true } - &ObjectCandidate | - &ProjectionCandidate => { + ObjectCandidate | + ProjectionCandidate => { // Arbitrarily give param candidates priority // over projection and object candidates. true }, - &ParamCandidate(..) => false, + ParamCandidate(..) => false, + }, + ImplCandidate(other_def) => { + // See if we can toss out `victim` based on specialization. + // This requires us to know *for sure* that the `other` impl applies + // i.e. EvaluatedToOk: + if other.evaluation == EvaluatedToOk { + if let ImplCandidate(victim_def) = victim.candidate { + return traits::specializes(self.tcx(), other_def, victim_def); + } + } + + false }, _ => false } diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs new file mode 100644 index 0000000000000..a692fe55a7789 --- /dev/null +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -0,0 +1,229 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Logic and data structures related to impl specialization, explained in +// greater detail below. +// +// At the moment, this implementation support only the simple "chain" rule: +// If any two impls overlap, one must be a strict subset of the other. +// +// See traits/README.md for a bit more detail on how specialization +// fits together with the rest of the trait machinery. + +use super::{SelectionContext, FulfillmentContext}; +use super::util::{fresh_type_vars_for_impl, impl_trait_ref_and_oblig}; + +use middle::cstore::CrateStore; +use middle::def_id::DefId; +use middle::infer::{self, InferCtxt, TypeOrigin}; +use middle::region; +use middle::subst::{Subst, Substs}; +use middle::traits::{self, ProjectionMode, ObligationCause, Normalized}; +use middle::ty::{self, TyCtxt}; +use syntax::codemap::DUMMY_SP; + +pub mod specialization_graph; + +/// Information pertinent to an overlapping impl error. +pub struct Overlap<'a, 'tcx: 'a> { + pub in_context: InferCtxt<'a, 'tcx>, + pub with_impl: DefId, + pub on_trait_ref: ty::TraitRef<'tcx>, +} + +/// Given a subst for the requested impl, translate it to a subst +/// appropriate for the actual item definition (whether it be in that impl, +/// a parent impl, or the trait). +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_substs` is a substitution of its generics (and +/// possibly some relevant `FnSpace` variables as well). And `target_node` is +/// the impl/trait we're actually going to get the definition from. The resulting +/// substitution will map from `target_node`'s generics to `source_impl`'s +/// generics as instantiated by `source_subst`. +/// +/// For example, consider the following scenario: +/// +/// ```rust +/// trait Foo { ... } +/// impl Foo for (T, U) { ... } // target impl +/// impl Foo for (V, V) { ... } // source impl +/// ``` +/// +/// Suppose we have selected "source impl" with `V` instantiated with `u32`. +/// This function will produce a substitution with `T` and `U` both mapping to `u32`. +/// +/// Where clauses add some trickiness here, because they can be used to "define" +/// an argument indirectly: +/// +/// ```rust +/// impl<'a, I, T: 'a> Iterator for Cloned +/// where I: Iterator, T: Clone +/// ``` +/// +/// In a case like this, the substitution for `T` is determined indirectly, +/// through associated type projection. We deal with such cases by using +/// *fulfillment* to relate the two impls, requiring that all projections are +/// resolved. +pub fn translate_substs<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + source_impl: DefId, + source_substs: &'tcx Substs<'tcx>, + target_node: specialization_graph::Node) + -> &'tcx Substs<'tcx> { + let source_trait_ref = infcx.tcx + .impl_trait_ref(source_impl) + .unwrap() + .subst(infcx.tcx, &source_substs); + + // translate the Self and TyParam parts of the substitution, since those + // vary across impls + let target_substs = match target_node { + specialization_graph::Node::Impl(target_impl) => { + // no need to translate if we're targetting the impl we started with + if source_impl == target_impl { + return source_substs; + } + + fulfill_implication(infcx, source_trait_ref, target_impl).unwrap_or_else(|_| { + infcx.tcx + .sess + .bug("When translating substitutions for specialization, the expected \ + specializaiton failed to hold") + }) + } + specialization_graph::Node::Trait(..) => source_trait_ref.substs.clone(), + }; + + // retain erasure mode + // NB: this must happen before inheriting method generics below + let target_substs = if source_substs.regions.is_erased() { + target_substs.erase_regions() + } else { + target_substs + }; + + // directly inherent the method generics, since those do not vary across impls + infcx.tcx.mk_substs(target_substs.with_method_from_subst(source_substs)) +} + +/// Is impl1 a specialization of impl2? +/// +/// Specialization is determined by the sets of types to which the impls apply; +/// impl1 specializes impl2 if it applies to a subset of the types impl2 applies +/// to. +pub fn specializes(tcx: &TyCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { + // The feature gate should prevent introducing new specializations, but not + // taking advantage of upstream ones. + if !tcx.sess.features.borrow().specialization && + (impl1_def_id.is_local() || impl2_def_id.is_local()) { + return false; + } + + // We determine whether there's a subset relationship by: + // + // - skolemizing impl1, + // - assuming the where clauses for impl1, + // - instantiating impl2 with fresh inference variables, + // - unifying, + // - attempting to prove the where clauses for impl2 + // + // The last three steps are encapsulated in `fulfill_implication`. + // + // See RFC 1210 for more details and justification. + + // Currently we do not allow e.g. a negative impl to specialize a positive one + if tcx.trait_impl_polarity(impl1_def_id) != tcx.trait_impl_polarity(impl2_def_id) { + return false; + } + + let mut infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Topmost); + + // create a parameter environment corresponding to a (skolemized) instantiation of impl1 + let scheme = tcx.lookup_item_type(impl1_def_id); + let predicates = tcx.lookup_predicates(impl1_def_id); + let mut penv = tcx.construct_parameter_environment(DUMMY_SP, + &scheme.generics, + &predicates, + region::DUMMY_CODE_EXTENT); + let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id) + .unwrap() + .subst(tcx, &penv.free_substs); + + // Normalize the trait reference, adding any obligations that arise into the impl1 assumptions + let Normalized { value: impl1_trait_ref, obligations: normalization_obligations } = { + let selcx = &mut SelectionContext::new(&infcx); + traits::normalize(selcx, ObligationCause::dummy(), &impl1_trait_ref) + }; + penv.caller_bounds.extend(normalization_obligations.into_iter().map(|o| o.predicate)); + + // Install the parameter environment, taking the predicates of impl1 as assumptions: + infcx.parameter_environment = penv; + + // Attempt to prove that impl2 applies, given all of the above. + fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok() +} + +/// Attempt to fulfill all obligations of `target_impl` after unification with +/// `source_trait_ref`. If successful, returns a substitution for *all* the +/// generics of `target_impl`, including both those needed to unify with +/// `source_trait_ref` and those whose identity is determined via a where +/// clause in the impl. +fn fulfill_implication<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + source_trait_ref: ty::TraitRef<'tcx>, + target_impl: DefId) + -> Result, ()> { + infcx.commit_if_ok(|_| { + let selcx = &mut SelectionContext::new(&infcx); + let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl); + let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx, + target_impl, + &target_substs); + + // do the impls unify? If not, no specialization. + if let Err(_) = infer::mk_eq_trait_refs(&infcx, + true, + TypeOrigin::Misc(DUMMY_SP), + source_trait_ref, + target_trait_ref) { + debug!("fulfill_implication: {:?} does not unify with {:?}", + source_trait_ref, + target_trait_ref); + return Err(()); + } + + // attempt to prove all of the predicates for impl2 given those for impl1 + // (which are packed up in penv) + + let mut fulfill_cx = FulfillmentContext::new(); + for oblig in obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, oblig); + } + + if let Err(errors) = infer::drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()) { + // no dice! + debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ + {:?}", + source_trait_ref, + target_trait_ref, + errors, + infcx.parameter_environment.caller_bounds); + Err(()) + } else { + debug!("fulfill_implication: an impl for {:?} specializes {:?}", + source_trait_ref, + target_trait_ref); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_type_vars_if_possible(&target_substs)) + } + }) +} diff --git a/src/librustc/middle/traits/specialize/specialization_graph.rs b/src/librustc/middle/traits/specialize/specialization_graph.rs new file mode 100644 index 0000000000000..f2170f75a11fe --- /dev/null +++ b/src/librustc/middle/traits/specialize/specialization_graph.rs @@ -0,0 +1,393 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cell; +use std::rc::Rc; + +use super::{Overlap, specializes}; + +use middle::cstore::CrateStore; +use middle::def_id::DefId; +use middle::infer; +use middle::traits::{self, ProjectionMode}; +use middle::ty::{self, TyCtxt, ImplOrTraitItem, TraitDef, TypeFoldable}; +use syntax::ast::Name; +use util::nodemap::DefIdMap; + +/// A per-trait graph of impls in specialization order. At the moment, this +/// graph forms a tree rooted with the trait itself, with all other nodes +/// representing impls, and parent-child relationships representing +/// specializations. +/// +/// The graph provides two key services: +/// +/// - Construction, which implicitly checks for overlapping impls (i.e., impls +/// that overlap but where neither specializes the other -- an artifact of the +/// simple "chain" rule. +/// +/// - Parent extraction. In particular, the graph can give you the *immediate* +/// parents of a given specializing impl, which is needed for extracting +/// default items amongst other thigns. In the simple "chain" rule, every impl +/// has at most one parent. +pub struct Graph { + // all impls have a parent; the "root" impls have as their parent the def_id + // of the trait + parent: DefIdMap, + + // the "root" impls are found by looking up the trait's def_id. + children: DefIdMap>, +} + +impl Graph { + pub fn new() -> Graph { + Graph { + parent: Default::default(), + children: Default::default(), + } + } + + /// Insert a local impl into the specialization graph. If an existing impl + /// conflicts with it (has overlap, but neither specializes the other), + /// information about the area of overlap is returned in the `Err`. + pub fn insert<'a, 'tcx>(&mut self, + tcx: &'a TyCtxt<'tcx>, + impl_def_id: DefId) + -> Result<(), Overlap<'a, 'tcx>> { + assert!(impl_def_id.is_local()); + + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let trait_def_id = trait_ref.def_id; + + debug!("insert({:?}): inserting TraitRef {:?} into specialization graph", + impl_def_id, trait_ref); + + // if the reference itself contains an earlier error (e.g., due to a + // resolution failure), then we just insert the impl at the top level of + // the graph and claim that there's no overlap (in order to supress + // bogus errors). + if trait_ref.references_error() { + debug!("insert: inserting dummy node for erroneous TraitRef {:?}, \ + impl_def_id={:?}, trait_def_id={:?}", + trait_ref, impl_def_id, trait_def_id); + + self.parent.insert(impl_def_id, trait_def_id); + self.children.entry(trait_def_id).or_insert(vec![]).push(impl_def_id); + return Ok(()); + } + + let mut parent = trait_def_id; + + // Ugly hack around borrowck limitations. Assigned only in the case + // where we bump downward an existing node in the graph. + let child_to_insert; + + 'descend: loop { + let mut possible_siblings = self.children.entry(parent).or_insert(vec![]); + + for slot in possible_siblings.iter_mut() { + let possible_sibling = *slot; + + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::Topmost); + let overlap = traits::overlapping_impls(&infcx, possible_sibling, impl_def_id); + + if let Some(impl_header) = overlap { + let le = specializes(tcx, impl_def_id, possible_sibling); + let ge = specializes(tcx, possible_sibling, impl_def_id); + + if le && !ge { + debug!("descending as child of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap()); + + // the impl specializes possible_sibling + parent = possible_sibling; + continue 'descend; + } else if ge && !le { + debug!("placing as parent of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap()); + + // possible_sibling specializes the impl + *slot = impl_def_id; + self.parent.insert(impl_def_id, parent); + self.parent.insert(possible_sibling, impl_def_id); + // we have to defer the insertion, because we can't + // relinquish the borrow of `self.children` + child_to_insert = possible_sibling; + break 'descend; + } else { + // overlap, but no specialization; error out + return Err(Overlap { + with_impl: possible_sibling, + on_trait_ref: impl_header.trait_ref.unwrap(), + in_context: infcx, + }); + } + } + } + + // no overlap with any potential siblings, so add as a new sibling + debug!("placing as new sibling"); + self.parent.insert(impl_def_id, parent); + possible_siblings.push(impl_def_id); + return Ok(()); + } + + self.children.insert(impl_def_id, vec![child_to_insert]); + Ok(()) + } + + /// Insert cached metadata mapping from a child impl back to its parent. + pub fn record_impl_from_cstore(&mut self, parent: DefId, child: DefId) { + if self.parent.insert(child, parent).is_some() { + panic!("When recording an impl from the crate store, information about its parent \ + was already present."); + } + + self.children.entry(parent).or_insert(vec![]).push(child); + } + + /// The parent of a given impl, which is the def id of the trait when the + /// impl is a "specialization root". + pub fn parent(&self, child: DefId) -> DefId { + *self.parent.get(&child).unwrap() + } +} + +/// A node in the specialization graph is either an impl or a trait +/// definition; either can serve as a source of item definitions. +/// There is always exactly one trait definition node: the root. +#[derive(Debug, Copy, Clone)] +pub enum Node { + Impl(DefId), + Trait(DefId), +} + +impl Node { + pub fn is_from_trait(&self) -> bool { + match *self { + Node::Trait(..) => true, + _ => false, + } + } + + /// Iterate over the items defined directly by the given (impl or trait) node. + pub fn items<'a, 'tcx>(&self, tcx: &'a TyCtxt<'tcx>) -> NodeItems<'a, 'tcx> { + match *self { + Node::Impl(impl_def_id) => { + NodeItems::Impl { + tcx: tcx, + items: cell::Ref::map(tcx.impl_items.borrow(), + |impl_items| &impl_items[&impl_def_id]), + idx: 0, + } + } + Node::Trait(trait_def_id) => { + NodeItems::Trait { + items: tcx.trait_items(trait_def_id).clone(), + idx: 0, + } + } + } + } + + pub fn def_id(&self) -> DefId { + match *self { + Node::Impl(did) => did, + Node::Trait(did) => did, + } + } +} + +/// An iterator over the items defined within a trait or impl. +pub enum NodeItems<'a, 'tcx: 'a> { + Impl { + tcx: &'a TyCtxt<'tcx>, + items: cell::Ref<'a, Vec>, + idx: usize, + }, + Trait { + items: Rc>>, + idx: usize, + }, +} + +impl<'a, 'tcx> Iterator for NodeItems<'a, 'tcx> { + type Item = ImplOrTraitItem<'tcx>; + fn next(&mut self) -> Option> { + match *self { + NodeItems::Impl { tcx, ref items, ref mut idx } => { + let items_table = tcx.impl_or_trait_items.borrow(); + if *idx < items.len() { + let item_def_id = items[*idx].def_id(); + let item = items_table[&item_def_id].clone(); + *idx += 1; + Some(item) + } else { + None + } + } + NodeItems::Trait { ref items, ref mut idx } => { + if *idx < items.len() { + let item = items[*idx].clone(); + *idx += 1; + Some(item) + } else { + None + } + } + } + } +} + +pub struct Ancestors<'a, 'tcx: 'a> { + trait_def: &'a TraitDef<'tcx>, + current_source: Option, +} + +impl<'a, 'tcx> Iterator for Ancestors<'a, 'tcx> { + type Item = Node; + fn next(&mut self) -> Option { + let cur = self.current_source.take(); + if let Some(Node::Impl(cur_impl)) = cur { + let parent = self.trait_def.specialization_graph.borrow().parent(cur_impl); + if parent == self.trait_def.def_id() { + self.current_source = Some(Node::Trait(parent)); + } else { + self.current_source = Some(Node::Impl(parent)); + } + } + cur + } +} + +pub struct NodeItem { + pub node: Node, + pub item: T, +} + +impl NodeItem { + pub fn map U>(self, f: F) -> NodeItem { + NodeItem { + node: self.node, + item: f(self.item), + } + } +} + +pub struct TypeDefs<'a, 'tcx: 'a> { + // generally only invoked once or twice, so the box doesn't hurt + iter: Box>>> + 'a>, +} + +impl<'a, 'tcx> Iterator for TypeDefs<'a, 'tcx> { + type Item = NodeItem>>; + fn next(&mut self) -> Option { + self.iter.next() + } +} + +pub struct FnDefs<'a, 'tcx: 'a> { + // generally only invoked once or twice, so the box doesn't hurt + iter: Box>>> + 'a>, +} + +impl<'a, 'tcx> Iterator for FnDefs<'a, 'tcx> { + type Item = NodeItem>>; + fn next(&mut self) -> Option { + self.iter.next() + } +} + +pub struct ConstDefs<'a, 'tcx: 'a> { + // generally only invoked once or twice, so the box doesn't hurt + iter: Box>>> + 'a>, +} + +impl<'a, 'tcx> Iterator for ConstDefs<'a, 'tcx> { + type Item = NodeItem>>; + fn next(&mut self) -> Option { + self.iter.next() + } +} + +impl<'a, 'tcx> Ancestors<'a, 'tcx> { + /// Search the items from the given ancestors, returning each type definition + /// with the given name. + pub fn type_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> TypeDefs<'a, 'tcx> { + let iter = self.flat_map(move |node| { + node.items(tcx) + .filter_map(move |item| { + if let ty::TypeTraitItem(assoc_ty) = item { + if assoc_ty.name == name { + return Some(NodeItem { + node: node, + item: assoc_ty, + }); + } + } + None + }) + + }); + TypeDefs { iter: Box::new(iter) } + } + + /// Search the items from the given ancestors, returning each fn definition + /// with the given name. + pub fn fn_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> FnDefs<'a, 'tcx> { + let iter = self.flat_map(move |node| { + node.items(tcx) + .filter_map(move |item| { + if let ty::MethodTraitItem(method) = item { + if method.name == name { + return Some(NodeItem { + node: node, + item: method, + }); + } + } + None + }) + + }); + FnDefs { iter: Box::new(iter) } + } + + /// Search the items from the given ancestors, returning each const + /// definition with the given name. + pub fn const_defs(self, tcx: &'a TyCtxt<'tcx>, name: Name) -> ConstDefs<'a, 'tcx> { + let iter = self.flat_map(move |node| { + node.items(tcx) + .filter_map(move |item| { + if let ty::ConstTraitItem(konst) = item { + if konst.name == name { + return Some(NodeItem { + node: node, + item: konst, + }); + } + } + None + }) + + }); + ConstDefs { iter: Box::new(iter) } + } +} + +/// Walk up the specialization ancestors of a given impl, starting with that +/// impl itself. +pub fn ancestors<'a, 'tcx>(trait_def: &'a TraitDef<'tcx>, + start_from_impl: DefId) + -> Ancestors<'a, 'tcx> { + Ancestors { + trait_def: trait_def, + current_source: Some(Node::Impl(start_from_impl)), + } +} diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 08d504143c7cc..1e37600bc0449 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -10,13 +10,13 @@ use middle::def_id::DefId; use middle::infer::InferCtxt; -use middle::subst::Substs; +use middle::subst::{Subst, Substs}; use middle::ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef}; use syntax::codemap::Span; use util::common::ErrorReported; use util::nodemap::FnvHashSet; -use super::{Obligation, ObligationCause, PredicateObligation}; +use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext, Normalized}; struct PredicateSet<'a,'tcx:'a> { tcx: &'a TyCtxt<'tcx>, @@ -299,6 +299,38 @@ impl<'tcx,I:Iterator>> Iterator for FilterToTraits { // Other /////////////////////////////////////////////////////////////////////////// +/// Instantiate all bound parameters of the impl with the given substs, +/// returning the resulting trait ref and all obligations that arise. +/// The obligations are closed under normalization. +pub fn impl_trait_ref_and_oblig<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, + impl_def_id: DefId, + impl_substs: &Substs<'tcx>) + -> (ty::TraitRef<'tcx>, + Vec>) +{ + let impl_trait_ref = + selcx.tcx().impl_trait_ref(impl_def_id).unwrap(); + let impl_trait_ref = + impl_trait_ref.subst(selcx.tcx(), impl_substs); + let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } = + super::normalize(selcx, ObligationCause::dummy(), &impl_trait_ref); + + let predicates = selcx.tcx().lookup_predicates(impl_def_id); + let predicates = predicates.instantiate(selcx.tcx(), impl_substs); + let Normalized { value: predicates, obligations: normalization_obligations2 } = + super::normalize(selcx, ObligationCause::dummy(), &predicates); + let impl_obligations = + predicates_for_generics(ObligationCause::dummy(), 0, &predicates); + + let impl_obligations: Vec<_> = + impl_obligations.into_iter() + .chain(normalization_obligations1) + .chain(normalization_obligations2) + .collect(); + + (impl_trait_ref, impl_obligations) +} + // determine the `self` type, using fresh variables for all variables // declared on the impl declaration e.g., `impl for Box<[(A,B)]>` // would return ($0, $1) where $0 and $1 are freshly instantiated type @@ -349,7 +381,6 @@ pub fn trait_ref_for_builtin_bound<'tcx>( } } - pub fn predicate_for_trait_ref<'tcx>( cause: ObligationCause<'tcx>, trait_ref: ty::TraitRef<'tcx>, diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 2491da7f5f317..081196835936c 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -281,6 +281,7 @@ pub struct Method<'tcx> { pub fty: BareFnTy<'tcx>, pub explicit_self: ExplicitSelfCategory, pub vis: hir::Visibility, + pub defaultness: hir::Defaultness, pub def_id: DefId, pub container: ImplOrTraitItemContainer, } @@ -292,16 +293,18 @@ impl<'tcx> Method<'tcx> { fty: BareFnTy<'tcx>, explicit_self: ExplicitSelfCategory, vis: hir::Visibility, + defaultness: hir::Defaultness, def_id: DefId, container: ImplOrTraitItemContainer) -> Method<'tcx> { - Method { + Method { name: name, generics: generics, predicates: predicates, fty: fty, explicit_self: explicit_self, vis: vis, + defaultness: defaultness, def_id: def_id, container: container, } @@ -334,6 +337,7 @@ pub struct AssociatedConst<'tcx> { pub name: Name, pub ty: Ty<'tcx>, pub vis: hir::Visibility, + pub defaultness: hir::Defaultness, pub def_id: DefId, pub container: ImplOrTraitItemContainer, pub has_value: bool @@ -344,6 +348,7 @@ pub struct AssociatedType<'tcx> { pub name: Name, pub ty: Option>, pub vis: hir::Visibility, + pub defaultness: hir::Defaultness, pub def_id: DefId, pub container: ImplOrTraitItemContainer, } @@ -2451,8 +2456,13 @@ impl<'tcx> TyCtxt<'tcx> { for impl_def_id in self.sess.cstore.implementations_of_trait(trait_id) { let impl_items = self.sess.cstore.impl_items(impl_def_id); let trait_ref = self.impl_trait_ref(impl_def_id).unwrap(); + // Record the trait->implementation mapping. - def.record_impl(self, impl_def_id, trait_ref); + if let Some(parent) = self.sess.cstore.impl_parent(impl_def_id) { + def.record_remote_impl(self, impl_def_id, trait_ref, parent); + } else { + def.record_remote_impl(self, impl_def_id, trait_ref, trait_id); + } // For any methods that use a default implementation, add them to // the map. This is a bit unfortunate. @@ -2660,7 +2670,6 @@ impl<'tcx> TyCtxt<'tcx> { Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone()) } - pub fn visit_all_items_in_krate(&self, dep_node_fn: F, visitor: &mut V) @@ -2668,6 +2677,16 @@ impl<'tcx> TyCtxt<'tcx> { { dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor); } + /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` + /// with the name of the crate containing the impl. + pub fn span_of_impl(&self, impl_did: DefId) -> Result { + if impl_did.is_local() { + let node_id = self.map.as_local_node_id(impl_did).unwrap(); + Ok(self.map.span(node_id)) + } else { + Err(self.sess.cstore.crate_name(impl_did.krate)) + } + } } /// The category of explicit self. @@ -2709,28 +2728,4 @@ impl<'tcx> TyCtxt<'tcx> { Some(d) => f(&d[..]) } } - - pub fn make_substs_for_receiver_types(&self, - trait_ref: &ty::TraitRef<'tcx>, - method: &ty::Method<'tcx>) - -> subst::Substs<'tcx> - { - /*! - * Substitutes the values for the receiver's type parameters - * that are found in method, leaving the method's type parameters - * intact. - */ - - let meth_tps: Vec = - method.generics.types.get_slice(subst::FnSpace) - .iter() - .map(|def| self.mk_param_from_def(def)) - .collect(); - let meth_regions: Vec = - method.generics.regions.get_slice(subst::FnSpace) - .iter() - .map(|def| def.to_early_bound_region()) - .collect(); - trait_ref.substs.clone().with_method(meth_tps, meth_regions) - } } diff --git a/src/librustc/middle/ty/sty.rs b/src/librustc/middle/ty/sty.rs index 2d7b7dc6e9b58..bbc5948f2cac7 100644 --- a/src/librustc/middle/ty/sty.rs +++ b/src/librustc/middle/ty/sty.rs @@ -1106,6 +1106,13 @@ impl<'tcx> TyS<'tcx> { } } + pub fn has_concrete_skeleton(&self) -> bool { + match self.sty { + TyParam(_) | TyInfer(_) | TyError => false, + _ => true, + } + } + // Returns the type and mutability of *ty. // // The parameter `explicit` indicates if this is an *explicit* dereference. diff --git a/src/librustc/middle/ty/trait_def.rs b/src/librustc/middle/ty/trait_def.rs index 5ecbbbcfbde6d..3d7b3bf263425 100644 --- a/src/librustc/middle/ty/trait_def.rs +++ b/src/librustc/middle/ty/trait_def.rs @@ -10,9 +10,10 @@ use dep_graph::DepNode; use middle::def_id::DefId; +use middle::traits::{self, specialization_graph}; use middle::ty; use middle::ty::fast_reject; -use middle::ty::{Ty, TyCtxt}; +use middle::ty::{Ty, TyCtxt, TraitRef}; use std::borrow::{Borrow}; use std::cell::{Cell, Ref, RefCell}; use syntax::ast::Name; @@ -59,6 +60,9 @@ pub struct TraitDef<'tcx> { /// Blanket impls associated with the trait. blanket_impls: RefCell>, + /// The specialization order for impls of this trait. + pub specialization_graph: RefCell, + /// Various flags pub flags: Cell } @@ -78,7 +82,8 @@ impl<'tcx> TraitDef<'tcx> { associated_type_names: associated_type_names, nonblanket_impls: RefCell::new(FnvHashMap()), blanket_impls: RefCell::new(vec![]), - flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS) + flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS), + specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()), } } @@ -114,11 +119,14 @@ impl<'tcx> TraitDef<'tcx> { tcx.dep_graph.read(DepNode::TraitImpls(self.trait_ref.def_id)); } - /// Records a trait-to-implementation mapping. - pub fn record_impl(&self, - tcx: &TyCtxt<'tcx>, - impl_def_id: DefId, - impl_trait_ref: ty::TraitRef<'tcx>) { + /// Records a basic trait-to-implementation mapping. + /// + /// Returns `true` iff the impl has not previously been recorded. + fn record_impl(&self, + tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: TraitRef<'tcx>) + -> bool { debug!("TraitDef::record_impl for {:?}, from {:?}", self, impl_trait_ref); @@ -134,22 +142,71 @@ impl<'tcx> TraitDef<'tcx> { impl_trait_ref.self_ty(), false) { if let Some(is) = self.nonblanket_impls.borrow().get(&sty) { if is.contains(&impl_def_id) { - return // duplicate - skip + return false; // duplicate - skip } } self.nonblanket_impls.borrow_mut().entry(sty).or_insert(vec![]).push(impl_def_id) } else { if self.blanket_impls.borrow().contains(&impl_def_id) { - return // duplicate - skip + return false; // duplicate - skip } self.blanket_impls.borrow_mut().push(impl_def_id) } + + true } - pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { - self.read_trait_impls(tcx); + /// Records a trait-to-implementation mapping for a crate-local impl. + pub fn record_local_impl(&self, + tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: TraitRef<'tcx>) { + assert!(impl_def_id.is_local()); + let was_new = self.record_impl(tcx, impl_def_id, impl_trait_ref); + assert!(was_new); + } + + /// Records a trait-to-implementation mapping for a non-local impl. + /// + /// The `parent_impl` is the immediately-less-specialized impl, or the + /// trait's def ID if the impl is is not a specialization -- information that + /// should be pulled from the metadata. + pub fn record_remote_impl(&self, + tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + impl_trait_ref: TraitRef<'tcx>, + parent_impl: DefId) { + assert!(!impl_def_id.is_local()); + + // if the impl has not previously been recorded + if self.record_impl(tcx, impl_def_id, impl_trait_ref) { + // if the impl is non-local, it's placed directly into the + // specialization graph using parent information drawn from metadata. + self.specialization_graph.borrow_mut() + .record_impl_from_cstore(parent_impl, impl_def_id) + } + } + + /// Adds a local impl into the specialization graph, returning an error with + /// overlap information if the impl overlaps but does not specialize an + /// existing impl. + pub fn add_impl_for_specialization<'a>(&self, + tcx: &'a TyCtxt<'tcx>, + impl_def_id: DefId) + -> Result<(), traits::Overlap<'a, 'tcx>> { + assert!(impl_def_id.is_local()); + + self.specialization_graph.borrow_mut() + .insert(tcx, impl_def_id) + } + pub fn ancestors<'a>(&'a self, of_impl: DefId) -> specialization_graph::Ancestors<'a, 'tcx> { + specialization_graph::ancestors(self, of_impl) + } + + pub fn for_each_impl(&self, tcx: &TyCtxt<'tcx>, mut f: F) { + self.read_trait_impls(tcx); tcx.populate_implementations_for_trait_if_necessary(self.trait_ref.def_id); for &impl_def_id in self.blanket_impls.borrow().iter() { @@ -223,4 +280,3 @@ bitflags! { const IMPLS_VALID = 1 << 3, } } - diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index c91441a3f8a4b..5af40a3675ff7 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -14,10 +14,10 @@ use back::svh::Svh; use middle::const_eval::{self, ConstVal, ErrKind}; use middle::const_eval::EvalHint::UncheckedExprHint; use middle::def_id::DefId; -use middle::subst::{self, Subst, Substs}; +use middle::subst; use middle::infer; use middle::pat_util; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable}; use middle::ty::{Disr, ParameterEnvironment}; use middle::ty::TypeVariants::*; @@ -26,7 +26,6 @@ use rustc_const_eval::{ConstInt, ConstIsize, ConstUsize}; use std::cmp; use std::hash::{Hash, SipHasher, Hasher}; -use std::rc::Rc; use syntax::ast::{self, Name}; use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt}; use syntax::codemap::Span; @@ -131,7 +130,10 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> { let tcx = self.tcx; // FIXME: (@jroesch) float this code up - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(self.clone())); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(self.clone()), + ProjectionMode::AnyFinal); let adt = match self_type.sty { ty::TyStruct(struct_def, substs) => { @@ -536,58 +538,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[derive(Debug)] -pub struct ImplMethod<'tcx> { - pub method: Rc>, - pub substs: &'tcx Substs<'tcx>, - pub is_provided: bool -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn get_impl_method(&self, - impl_def_id: DefId, - substs: &'tcx Substs<'tcx>, - name: Name) - -> ImplMethod<'tcx> - { - // there don't seem to be nicer accessors to these: - let impl_or_trait_items_map = self.impl_or_trait_items.borrow(); - - for impl_item in &self.impl_items.borrow()[&impl_def_id] { - if let ty::MethodTraitItem(ref meth) = - impl_or_trait_items_map[&impl_item.def_id()] { - if meth.name == name { - return ImplMethod { - method: meth.clone(), - substs: substs, - is_provided: false - } - } - } - } - - // It is not in the impl - get the default from the trait. - let trait_ref = self.impl_trait_ref(impl_def_id).unwrap(); - for trait_item in self.trait_items(trait_ref.def_id).iter() { - if let &ty::MethodTraitItem(ref meth) = trait_item { - if meth.name == name { - let impl_to_trait_substs = self - .make_substs_for_receiver_types(&trait_ref, meth); - let substs = impl_to_trait_substs.subst(self, substs); - return ImplMethod { - method: meth.clone(), - substs: self.mk_substs(substs), - is_provided: true - } - } - } - } - - self.sess.bug(&format!("method {:?} not found in {:?}", - name, impl_def_id)) - } -} - impl<'tcx> ty::TyS<'tcx> { fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>, bound: ty::BuiltinBound, @@ -595,7 +545,10 @@ impl<'tcx> ty::TyS<'tcx> { -> bool { let tcx = param_env.tcx; - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env.clone())); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(param_env.clone()), + ProjectionMode::AnyFinal); let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, self, bound, span); diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 4a1c115e65599..9186765e6d02c 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -27,6 +27,7 @@ use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::middle::region; use rustc::middle::ty::{self, TyCtxt}; +use rustc::middle::traits::ProjectionMode; use syntax::ast; use syntax::codemap::Span; use rustc_front::hir; @@ -202,7 +203,10 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, debug!("check_loans(body id={})", body.id); let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); - let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(bccx.tcx, + &bccx.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); let mut clcx = CheckLoanCtxt { bccx: bccx, diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index e2543b289103a..2d255c054548f 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -24,6 +24,7 @@ use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; use rustc::middle::region; use rustc::middle::ty::{self, TyCtxt}; +use rustc::middle::traits::ProjectionMode; use syntax::ast; use syntax::codemap::Span; @@ -55,7 +56,10 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, }; let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); - let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(bccx.tcx, + &bccx.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); { let mut euv = euv::ExprUseVisitor::new(&mut glcx, &infcx); euv.walk_fn(decl, body); @@ -525,7 +529,10 @@ struct StaticInitializerCtxt<'a, 'tcx: 'a> { impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> { fn visit_expr(&mut self, ex: &Expr) { if let hir::ExprAddrOf(mutbl, ref base) = ex.node { - let infcx = infer::new_infer_ctxt(self.bccx.tcx, &self.bccx.tcx.tables, None); + let infcx = infer::new_infer_ctxt(self.bccx.tcx, + &self.bccx.tcx.tables, + None, + ProjectionMode::AnyFinal); let mc = mc::MemCategorizationContext::new(&infcx); let base_cmt = mc.cat_expr(&base).unwrap(); let borrow_kind = ty::BorrowKind::from_mutbl(mutbl); diff --git a/src/librustc_data_structures/obligation_forest/README.md b/src/librustc_data_structures/obligation_forest/README.md index d76d7f6ba340e..982a2bacce164 100644 --- a/src/librustc_data_structures/obligation_forest/README.md +++ b/src/librustc_data_structures/obligation_forest/README.md @@ -60,7 +60,7 @@ which includes three bits of information: `process_obligations` would simply yield back further ambiguous results. This is used by the `FulfillmentContext` to decide when it has reached a steady state. - + #### Snapshots The `ObligationForest` supports a limited form of snapshots; see @@ -79,5 +79,3 @@ parent and (for convenience) its root (which may be itself). It also has a current state, described by `NodeState`. After each processing step, we compress the vector to remove completed and error nodes, which aren't needed anymore. - - diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 3cab9cfb88ca9..437672c551464 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -22,6 +22,7 @@ use rustc_typeck::middle::resolve_lifetime; use rustc_typeck::middle::stability; use rustc_typeck::middle::subst; use rustc_typeck::middle::subst::Subst; +use rustc_typeck::middle::traits::ProjectionMode; use rustc_typeck::middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_typeck::middle::ty::relate::TypeRelation; use rustc_typeck::middle::infer::{self, TypeOrigin}; @@ -143,7 +144,10 @@ fn test_env(source_string: &str, lang_items, index, |tcx| { - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + None, + ProjectionMode::AnyFinal); body(Env { infcx: &infcx }); let free_regions = FreeRegionMap::new(); infcx.resolve_regions_and_report_errors(&free_regions, diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs index beedb3d70b699..6ae59122f71c4 100644 --- a/src/librustc_front/fold.rs +++ b/src/librustc_front/fold.rs @@ -839,6 +839,7 @@ pub fn noop_fold_impl_item(i: ImplItem, folder: &mut T) -> ImplItem { name: folder.fold_name(i.name), attrs: fold_attrs(i.attrs, folder), vis: i.vis, + defaultness: i.defaultness, node: match i.node { ImplItemKind::Const(ty, expr) => { ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr)) diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index cc7c0f7865ea5..0b1418fc87845 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -864,10 +864,10 @@ pub struct MethodSig { pub explicit_self: ExplicitSelf, } -/// Represents a method declaration in a trait declaration, possibly including -/// a default implementation A trait method is either required (meaning it -/// doesn't have an implementation, just a signature) or provided (meaning it -/// has a default implementation). +/// Represents an item declaration within a trait declaration, +/// possibly including a default implementation. A trait item is +/// either required (meaning it doesn't have an implementation, just a +/// signature) or provided (meaning it has a default implementation). #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct TraitItem { pub id: NodeId, @@ -889,6 +889,7 @@ pub struct ImplItem { pub id: NodeId, pub name: Name, pub vis: Visibility, + pub defaultness: Defaultness, pub attrs: HirVec, pub node: ImplItemKind, pub span: Span, @@ -1046,6 +1047,22 @@ pub enum Constness { NotConst, } +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum Defaultness { + Default, + Final, +} + +impl Defaultness { + pub fn is_final(&self) -> bool { + *self == Defaultness::Final + } + + pub fn is_default(&self) -> bool { + *self == Defaultness::Default + } +} + impl fmt::Display for Unsafety { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(match *self { diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index 291df66755e7d..825ab3fbd4c82 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -756,6 +756,7 @@ pub fn lower_impl_item(lctx: &LoweringContext, i: &ImplItem) -> hir::ImplItem { name: i.ident.name, attrs: lower_attrs(lctx, &i.attrs), vis: lower_visibility(lctx, i.vis), + defaultness: lower_defaultness(lctx, i.defaultness), node: match i.node { ImplItemKind::Const(ref ty, ref expr) => { hir::ImplItemKind::Const(lower_ty(lctx, ty), lower_expr(lctx, expr)) @@ -1707,6 +1708,13 @@ pub fn lower_visibility(_lctx: &LoweringContext, v: Visibility) -> hir::Visibili } } +pub fn lower_defaultness(_lctx: &LoweringContext, d: Defaultness) -> hir::Defaultness { + match d { + Defaultness::Default => hir::Defaultness::Default, + Defaultness::Final => hir::Defaultness::Final, + } +} + pub fn lower_block_check_mode(lctx: &LoweringContext, b: &BlockCheckMode) -> hir::BlockCheckMode { match *b { BlockCheckMode::Default => hir::DefaultBlock, diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs index 143dfce09b602..1100f084454c7 100644 --- a/src/librustc_front/print/pprust.rs +++ b/src/librustc_front/print/pprust.rs @@ -1014,6 +1014,11 @@ impl<'a> State<'a> { try!(self.hardbreak_if_not_bol()); try!(self.maybe_print_comment(ii.span.lo)); try!(self.print_outer_attributes(&ii.attrs)); + + if let hir::Defaultness::Default = ii.defaultness { + try!(self.word_nbsp("default")); + } + match ii.node { hir::ImplItemKind::Const(ref ty, ref expr) => { try!(self.print_associated_const(ii.name, &ty, Some(&expr), ii.vis)); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 0c906f8eb546c..88027931022e7 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -35,6 +35,7 @@ use middle::def_id::DefId; use middle::subst::Substs; use middle::ty::{self, Ty, TyCtxt}; use middle::ty::adjustment; +use middle::traits::ProjectionMode; use rustc::front::map as hir_map; use util::nodemap::{NodeSet}; use lint::{Level, LateContext, LintContext, LintArray, Lint}; @@ -868,7 +869,10 @@ impl LateLintPass for UnconditionalRecursion { let node_id = tcx.map.as_local_node_id(method.def_id).unwrap(); let param_env = ty::ParameterEnvironment::for_item(tcx, node_id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); let mut selcx = traits::SelectionContext::new(&infcx); match selcx.select(&obligation) { // The method comes from a `T: Trait` bound. diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs index 991cbe137ecf9..a0cbba279acc0 100644 --- a/src/librustc_metadata/common.rs +++ b/src/librustc_metadata/common.rs @@ -241,6 +241,10 @@ pub const tag_items_data_item_constness: usize = 0xa6; pub const tag_items_data_item_deprecation: usize = 0xa7; +pub const tag_items_data_item_defaultness: usize = 0xa8; + +pub const tag_items_data_parent_impl: usize = 0xa9; + pub const tag_rustc_version: usize = 0x10f; pub fn rustc_version() -> String { format!( diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index b3f24b8f16b16..2cd119cfc48be 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -225,6 +225,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { decoder::get_associated_consts(self.intr.clone(), &cdata, def.index, tcx) } + fn impl_parent(&self, impl_def: DefId) -> Option { + let cdata = self.get_crate_data(impl_def.krate); + decoder::get_parent_impl(&*cdata, impl_def.index) + } + fn trait_of_item(&self, tcx: &TyCtxt<'tcx>, def_id: DefId) -> Option { let cdata = self.get_crate_data(def_id.krate); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index dfc794dc5b874..38a2a7794bcbd 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -165,6 +165,19 @@ fn fn_constness(item: rbml::Doc) -> hir::Constness { } } +fn item_defaultness(item: rbml::Doc) -> hir::Defaultness { + match reader::maybe_get_doc(item, tag_items_data_item_defaultness) { + None => hir::Defaultness::Default, // should occur only for default impls on traits + Some(defaultness_doc) => { + match reader::doc_as_u8(defaultness_doc) as char { + 'd' => hir::Defaultness::Default, + 'f' => hir::Defaultness::Final, + _ => panic!("unknown defaultness character") + } + } + } +} + fn item_sort(item: rbml::Doc) -> Option { reader::tagged_docs(item, tag_item_trait_item_sort).nth(0).map(|doc| { doc.as_str_slice().as_bytes()[0] as char @@ -551,6 +564,13 @@ pub fn get_visibility(cdata: Cmd, id: DefIndex) -> hir::Visibility { item_visibility(cdata.lookup_item(id)) } +pub fn get_parent_impl(cdata: Cmd, id: DefIndex) -> Option { + let item = cdata.lookup_item(id); + reader::maybe_get_doc(item, tag_items_data_parent_impl).map(|doc| { + translated_def_id(cdata, doc) + }) +} + pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec { let item = cdata.lookup_item(id); match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| { @@ -976,6 +996,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, let name = item_name(&intr, item_doc); let vis = item_visibility(item_doc); + let defaultness = item_defaultness(item_doc); match item_sort(item_doc) { sort @ Some('C') | sort @ Some('c') => { @@ -984,6 +1005,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, name: name, ty: ty, vis: vis, + defaultness: defaultness, def_id: def_id, container: container, has_value: sort == Some('C') @@ -1007,6 +1029,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, fty, explicit_self, vis, + defaultness, def_id, container))) } @@ -1016,6 +1039,7 @@ pub fn get_impl_or_trait_item<'tcx>(intr: Rc, name: name, ty: ty, vis: vis, + defaultness: defaultness, def_id: def_id, container: container, })) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 3dc0e53b254c4..41baa0b159148 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -25,6 +25,7 @@ use middle::def_id::{CRATE_DEF_INDEX, DefId}; use middle::dependency_format::Linkage; use middle::stability; use middle::subst; +use middle::traits::specialization_graph; use middle::ty::{self, Ty, TyCtxt}; use middle::ty::util::IntTypeExt; @@ -451,6 +452,14 @@ fn encode_constness(rbml_w: &mut Encoder, constness: hir::Constness) { rbml_w.end_tag(); } +fn encode_defaultness(rbml_w: &mut Encoder, defaultness: hir::Defaultness) { + let ch = match defaultness { + hir::Defaultness::Default => 'd', + hir::Defaultness::Final => 'f', + }; + rbml_w.wr_tagged_u8(tag_items_data_item_defaultness, ch as u8); +} + fn encode_explicit_self(rbml_w: &mut Encoder, explicit_self: &ty::ExplicitSelfCategory) { let tag = tag_item_trait_method_explicit_self; @@ -674,6 +683,7 @@ fn encode_info_for_associated_const<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, if let Some(ii) = impl_item_opt { encode_attributes(rbml_w, &ii.attrs); + encode_defaultness(rbml_w, ii.defaultness); encode_inlined_item(ecx, rbml_w, InlinedItemRef::ImplItem(ecx.tcx.map.local_def_id(parent_id), @@ -725,6 +735,7 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, impl_item)); } encode_constness(rbml_w, sig.constness); + encode_defaultness(rbml_w, impl_item.defaultness); if !any_types { let m_id = ecx.local_id(m.def_id); encode_symbol(ecx, rbml_w, m_id); @@ -767,6 +778,7 @@ fn encode_info_for_associated_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, if let Some(ii) = impl_item_opt { encode_attributes(rbml_w, &ii.attrs); + encode_defaultness(rbml_w, ii.defaultness); } else { encode_predicates(rbml_w, ecx, index, &ecx.tcx.lookup_predicates(associated_type.def_id), @@ -873,6 +885,12 @@ fn encode_deprecation(rbml_w: &mut Encoder, depr_opt: Option) }); } +fn encode_parent_impl(rbml_w: &mut Encoder, parent_opt: Option) { + parent_opt.map(|parent| { + rbml_w.wr_tagged_u64(tag_items_data_parent_impl, def_to_u64(parent)); + }); +} + fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, xrefs: FnvHashMap, u32>) @@ -1150,8 +1168,19 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, } rbml_w.end_tag(); } - if let Some(trait_ref) = tcx.impl_trait_ref(ecx.tcx.map.local_def_id(item.id)) { + let did = ecx.tcx.map.local_def_id(item.id); + if let Some(trait_ref) = tcx.impl_trait_ref(did) { encode_trait_ref(rbml_w, ecx, trait_ref, tag_item_trait_ref); + + let trait_def = tcx.lookup_trait_def(trait_ref.def_id); + let parent = trait_def.ancestors(did) + .skip(1) + .next() + .and_then(|node| match node { + specialization_graph::Node::Impl(parent) => Some(parent), + _ => None, + }); + encode_parent_impl(rbml_w, parent); } encode_path(rbml_w, path.clone()); encode_stability(rbml_w, stab); diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 2e13e7b42bd6c..13521de78af28 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -27,6 +27,7 @@ use hair::cx::Cx; use rustc::mir::mir_map::MirMap; use rustc::middle::infer; use rustc::middle::region::CodeExtentData; +use rustc::middle::traits::ProjectionMode; use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::util::common::ErrorReported; use rustc::util::nodemap::NodeMap; @@ -137,7 +138,11 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { }; let param_env = ty::ParameterEnvironment::for_item(self.tcx, id); - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); + match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) { Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()), Err(ErrorReported) => {} diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 45393d57101e5..d99e6ff4bf55d 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -13,7 +13,7 @@ use rustc::dep_graph::DepNode; use rustc::middle::infer::{self, InferCtxt}; -use rustc::middle::traits; +use rustc::middle::traits::{self, ProjectionMode}; use rustc::middle::ty::fold::TypeFoldable; use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::mir::repr::*; @@ -582,7 +582,10 @@ impl<'tcx> MirPass<'tcx> for TypeckMir { } let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id)); let param_env = ty::ParameterEnvironment::for_item(tcx, id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); let mut checker = TypeChecker::new(&infcx); { let mut verifier = TypeVerifier::new(&mut checker, mir); diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index a9e03bba7db8e..6be7f6c200247 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -35,8 +35,8 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::infer; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; -use rustc::middle::traits; use rustc::middle::ty::{self, Ty, TyCtxt}; +use rustc::middle::traits::{self, ProjectionMode}; use rustc::util::nodemap::NodeMap; use rustc::middle::const_qualif::ConstQualif; use rustc::lint::builtin::CONST_ERR; @@ -92,7 +92,10 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { None => self.tcx.empty_parameter_environment() }; - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env)); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + Some(param_env), + ProjectionMode::AnyFinal); f(&mut euv::ExprUseVisitor::new(self, &infcx)) } @@ -247,7 +250,10 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { fn check_static_type(&self, e: &hir::Expr) { let ty = self.tcx.node_id_to_type(e.id); - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + None, + ProjectionMode::AnyFinal); let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic); let mut fulfillment_cx = traits::FulfillmentContext::new(); fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause); diff --git a/src/librustc_passes/rvalues.rs b/src/librustc_passes/rvalues.rs index 7eef69ca50fd0..88048b514e1f5 100644 --- a/src/librustc_passes/rvalues.rs +++ b/src/librustc_passes/rvalues.rs @@ -16,6 +16,7 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::infer; use rustc::middle::mem_categorization as mc; use rustc::middle::ty::{self, TyCtxt, ParameterEnvironment}; +use rustc::middle::traits::ProjectionMode; use rustc_front::hir; use rustc_front::intravisit; @@ -43,7 +44,8 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for RvalueContext<'a, 'tcx> { let param_env = ParameterEnvironment::for_item(self.tcx, fn_id); let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, - Some(param_env.clone())); + Some(param_env.clone()), + ProjectionMode::AnyFinal); let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: ¶m_env }; let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx); euv.walk_fn(fd, b); diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index d1567cc6fa543..f5fbec0b1879e 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -217,6 +217,7 @@ use trans::tvec; use trans::type_of; use trans::Disr; use middle::ty::{self, Ty, TyCtxt}; +use middle::traits::ProjectionMode; use session::config::NoDebugInfo; use util::common::indenter; use util::nodemap::FnvHashMap; @@ -1475,7 +1476,9 @@ fn is_discr_reassigned(bcx: Block, discr: &hir::Expr, body: &hir::Expr) -> bool reassigned: false }; { - let infcx = infer::normalizing_infer_ctxt(bcx.tcx(), &bcx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(bcx.tcx(), + &bcx.tcx().tables, + ProjectionMode::Any); let mut visitor = euv::ExprUseVisitor::new(&mut rc, &infcx); visitor.walk_expr(body); } diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs index 009d43e813ebc..d93d32f8e0d06 100644 --- a/src/librustc_trans/trans/attributes.rs +++ b/src/librustc_trans/trans/attributes.rs @@ -13,6 +13,7 @@ use libc::{c_uint, c_ulonglong}; use llvm::{self, ValueRef, AttrHelper}; use middle::ty; use middle::infer; +use middle::traits::ProjectionMode; use session::config::NoDebugInfo; use syntax::abi::Abi; pub use syntax::attr::InlineAttr; @@ -133,7 +134,9 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx let (fn_sig, abi, env_ty) = match fn_type.sty { ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => (&f.sig, f.abi, None), ty::TyClosure(closure_did, ref substs) => { - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), + &ccx.tcx().tables, + ProjectionMode::Any); function_type = infcx.closure_type(closure_did, substs); let self_type = base::self_type_for_closure(ccx, closure_did, fn_type); (&function_type.sig, Abi::RustCall, Some(self_type)) diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index 95ca250e84445..11c03fe7a7dc7 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -13,6 +13,7 @@ use back::link::{self, mangle_internal_name_by_path_and_seq}; use llvm::{ValueRef, get_params}; use middle::def_id::DefId; use middle::infer; +use middle::traits::ProjectionMode; use trans::adt; use trans::attributes; use trans::base::*; @@ -206,7 +207,7 @@ pub fn trans_closure_expr<'a, 'tcx>(dest: Dest<'a, 'tcx>, // this function (`trans_closure`) is invoked at the point // of the closure expression. - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any); let function_type = infcx.closure_type(closure_def_id, closure_substs); let freevars: Vec = @@ -329,7 +330,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( ccx.tn().val_to_string(llreffn)); let tcx = ccx.tcx(); - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables, ProjectionMode::Any); // Find a version of the closure type. Substitute static for the // region since it doesn't really matter. diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs index abfd127f38860..cea97c1a1e77f 100644 --- a/src/librustc_trans/trans/collector.rs +++ b/src/librustc_trans/trans/collector.rs @@ -819,10 +819,11 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, nested: _ }) => { let callee_substs = impl_substs.with_method_from(&rcvr_substs); - let impl_method = tcx.get_impl_method(impl_did, - tcx.mk_substs(callee_substs), - trait_method.name); - Some((impl_method.method.def_id, impl_method.substs)) + let impl_method = meth::get_impl_method(tcx, + impl_did, + tcx.mk_substs(callee_substs), + trait_method.name); + Some((impl_method.method.def_id, &impl_method.substs)) } // If we have a closure or a function pointer, we will also encounter // the concrete closure/function somewhere else (during closure or fn @@ -982,7 +983,7 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, if can_have_local_instance(ccx, impl_method.method.def_id) { Some(create_fn_trans_item(ccx, impl_method.method.def_id, - impl_method.substs, + &impl_method.substs, &Substs::trans_empty())) } else { None @@ -1160,13 +1161,14 @@ fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let mth = tcx.get_impl_method(impl_def_id, - callee_substs, - default_impl.name); + let mth = meth::get_impl_method(tcx, + impl_def_id, + callee_substs, + default_impl.name); assert!(mth.is_provided); - let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs); + let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); if !normalize_and_test_predicates(ccx, predicates.into_vec()) { continue; } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 34ef4f4acec5e..0aa69dec253a1 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -37,8 +37,8 @@ use trans::machine; use trans::monomorphize; use trans::type_::Type; use trans::type_of; -use middle::traits; use middle::ty::{self, Ty, TyCtxt}; +use middle::traits::{self, SelectionContext, ProjectionMode}; use middle::ty::fold::{TypeFolder, TypeFoldable}; use rustc_front::hir; use rustc::mir::repr::Mir; @@ -1137,8 +1137,8 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut selcx = traits::SelectionContext::new(&infcx); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); + let mut selcx = SelectionContext::new(&infcx); let obligation = traits::Obligation::new(traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID), @@ -1198,8 +1198,8 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, predicates); let tcx = ccx.tcx(); - let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); - let mut selcx = traits::SelectionContext::new(&infcx); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); + let mut selcx = SelectionContext::new(&infcx); let mut fulfill_cx = traits::FulfillmentContext::new(); let cause = traits::ObligationCause::dummy(); let traits::Normalized { value: predicates, obligations } = diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index 38e456c068829..0c512200ff3d5 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -22,6 +22,7 @@ use llvm::{self, ValueRef}; use middle::ty; use middle::infer; +use middle::traits::ProjectionMode; use syntax::abi::Abi; use trans::attributes; use trans::base; @@ -111,7 +112,9 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, (&f.sig, f.abi, None) } ty::TyClosure(closure_did, ref substs) => { - let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); + let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), + &ccx.tcx().tables, + ProjectionMode::Any); function_type = infcx.closure_type(closure_did, substs); let self_type = base::self_type_for_closure(ccx, closure_did, fn_type); let llenvironment_type = type_of::type_of_explicit_arg(ccx, self_type); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 78b86dafa18ad..7397ccc2505f1 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::rc::Rc; + use arena::TypedArena; use back::link; use llvm::{ValueRef, get_params}; @@ -15,7 +17,7 @@ use middle::def_id::DefId; use middle::infer; use middle::subst::{Subst, Substs}; use middle::subst; -use middle::traits; +use middle::traits::{self, ProjectionMode}; use trans::base::*; use trans::build::*; use trans::callee::{Callee, Virtual, ArgVals, @@ -31,9 +33,9 @@ use trans::glue; use trans::machine; use trans::type_::Type; use trans::type_of::*; -use middle::ty::{self, Ty, TyCtxt}; +use middle::ty::{self, Ty, TyCtxt, TypeFoldable}; -use syntax::ast; +use syntax::ast::{self, Name}; use syntax::attr; use syntax::codemap::DUMMY_SP; @@ -107,7 +109,7 @@ pub fn callee_for_trait_impl<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // those from the impl and those from the method: let impl_substs = vtable_impl.substs.with_method_from(&substs); let substs = ccx.tcx().mk_substs(impl_substs); - let mth = ccx.tcx().get_impl_method(impl_did, substs, mname); + let mth = get_impl_method(ccx.tcx(), impl_did, substs, mname); // Translate the function, bypassing Callee::def. // That is because default methods have the same ID as the @@ -315,7 +317,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, trans_fn_ref_with_substs(ccx, mth.method.def_id, None, - mth.substs).val + &mth.substs).val } None => nullptr } @@ -378,7 +380,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, impl_id: DefId, substs: &'tcx subst::Substs<'tcx>) - -> Vec>> + -> Vec>> { let tcx = ccx.tcx(); @@ -428,7 +430,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. - let mth = tcx.get_impl_method(impl_id, substs, name); + let mth = get_impl_method(tcx, impl_id, substs, name); debug!("get_vtable_methods: mth={:?}", mth); @@ -438,7 +440,7 @@ pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // method could then never be called, so we do not want to // try and trans it, in that case. Issue #23435. if mth.is_provided { - let predicates = mth.method.predicates.predicates.subst(tcx, mth.substs); + let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); if !normalize_and_test_predicates(ccx, predicates.into_vec()) { debug!("get_vtable_methods: predicates do not hold"); return None; @@ -466,3 +468,37 @@ fn opaque_method_ty<'tcx>(tcx: &TyCtxt<'tcx>, method_ty: &ty::BareFnTy<'tcx>) }), }) } + +#[derive(Debug)] +pub struct ImplMethod<'tcx> { + pub method: Rc>, + pub substs: &'tcx Substs<'tcx>, + pub is_provided: bool +} + +/// Locates the applicable definition of a method, given its name. +pub fn get_impl_method<'tcx>(tcx: &TyCtxt<'tcx>, + impl_def_id: DefId, + substs: &'tcx Substs<'tcx>, + name: Name) + -> ImplMethod<'tcx> +{ + assert!(!substs.types.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.lookup_trait_def(trait_def_id); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables, ProjectionMode::Any); + + match trait_def.ancestors(impl_def_id).fn_defs(tcx, name).next() { + Some(node_item) => { + ImplMethod { + method: node_item.item, + substs: traits::translate_substs(&infcx, impl_def_id, substs, node_item.node), + is_provided: node_item.node.is_from_trait(), + } + } + None => { + tcx.sess.bug(&format!("method {:?} not found in {:?}", name, impl_def_id)) + } + } +} diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index ff7b809577f64..899f79b3dff94 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -10,8 +10,8 @@ use middle::free_region::FreeRegionMap; use middle::infer::{self, TypeOrigin}; -use middle::traits; use middle::ty::{self, TyCtxt}; +use middle::traits::{self, ProjectionMode}; use middle::subst::{self, Subst, Substs, VecPerParamSpace}; use syntax::ast; @@ -42,7 +42,7 @@ pub fn compare_impl_method<'tcx>(tcx: &TyCtxt<'tcx>, debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}", impl_trait_ref); - let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); let mut fulfillment_cx = traits::FulfillmentContext::new(); let trait_to_impl_substs = &impl_trait_ref.substs; @@ -416,7 +416,7 @@ pub fn compare_const_impl<'tcx>(tcx: &TyCtxt<'tcx>, debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); let mut fulfillment_cx = traits::FulfillmentContext::new(); // The below is for the most part highly similar to the procedure diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 4ebe4c25dd1d3..4ed1bab46b2d9 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -15,8 +15,8 @@ use middle::free_region::FreeRegionMap; use middle::infer; use middle::region; use middle::subst::{self, Subst}; -use middle::traits; use middle::ty::{self, Ty, TyCtxt}; +use middle::traits::{self, ProjectionMode}; use util::nodemap::FnvHashSet; use syntax::ast; @@ -82,7 +82,10 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( // check that the impl type can be made to match the trait type. let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(impl_param_env)); + let infcx = infer::new_infer_ctxt(tcx, + &tcx.tables, + Some(impl_param_env), + ProjectionMode::AnyFinal); let mut fulfillment_cx = traits::FulfillmentContext::new(); let named_type = tcx.lookup_item_type(self_type_did).ty; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 19731407d9e7a..0d5e25efd68c9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -92,7 +92,7 @@ use middle::infer; use middle::infer::{TypeOrigin, TypeTrace, type_variable}; use middle::pat_util::{self, pat_id_map}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; -use middle::traits::{self, report_fulfillment_errors}; +use middle::traits::{self, report_fulfillment_errors, ProjectionMode}; use middle::ty::{GenericPredicates, TypeScheme}; use middle::ty::{ParamTy, ParameterEnvironment}; use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue}; @@ -307,7 +307,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { -> Inherited<'a, 'tcx> { Inherited { - infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env)), + infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), ProjectionMode::AnyFinal), fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), locals: RefCell::new(NodeMap()), tables: tables, @@ -672,10 +672,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) { hir::ItemFn(..) => {} // entirely within check_item_body hir::ItemImpl(_, _, _, _, _, ref impl_items) => { debug!("ItemImpl {} with id {}", it.name, it.id); - match ccx.tcx.impl_trait_ref(ccx.tcx.map.local_def_id(it.id)) { + let impl_def_id = ccx.tcx.map.local_def_id(it.id); + match ccx.tcx.impl_trait_ref(impl_def_id) { Some(impl_trait_ref) => { check_impl_items_against_trait(ccx, it.span, + impl_def_id, &impl_trait_ref, impl_items); } @@ -862,12 +864,71 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env); } +fn report_forbidden_specialization(tcx: &TyCtxt, + impl_item: &hir::ImplItem, + parent_impl: DefId) +{ + let mut err = struct_span_err!( + tcx.sess, impl_item.span, E0520, + "item `{}` is provided by an `impl` that specializes \ + another, but the item in the parent `impl` is not \ + marked `default` and so it cannot be specialized.", + impl_item.name); + + match tcx.span_of_impl(parent_impl) { + Ok(span) => { + err.span_note(span, "parent implementation is here:"); + } + Err(cname) => { + err.note(&format!("parent implementation is in crate `{}`", cname)); + } + } + + err.emit(); +} + +fn check_specialization_validity<'tcx>(tcx: &TyCtxt<'tcx>, trait_def: &ty::TraitDef<'tcx>, + impl_id: DefId, impl_item: &hir::ImplItem) +{ + let ancestors = trait_def.ancestors(impl_id); + + let parent = match impl_item.node { + hir::ImplItemKind::Const(..) => { + ancestors.const_defs(tcx, impl_item.name).skip(1).next() + .map(|node_item| node_item.map(|parent| parent.defaultness)) + } + hir::ImplItemKind::Method(..) => { + ancestors.fn_defs(tcx, impl_item.name).skip(1).next() + .map(|node_item| node_item.map(|parent| parent.defaultness)) + + } + hir::ImplItemKind::Type(_) => { + ancestors.type_defs(tcx, impl_item.name).skip(1).next() + .map(|node_item| node_item.map(|parent| parent.defaultness)) + } + }; + + if let Some(parent) = parent { + if parent.item.is_final() { + report_forbidden_specialization(tcx, impl_item, parent.node.def_id()); + } + } + +} + fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_span: Span, + impl_id: DefId, impl_trait_ref: &ty::TraitRef<'tcx>, impl_items: &[hir::ImplItem]) { - // Locate trait methods + // If the trait reference itself is erroneous (so the compilation is going + // to fail), skip checking the items here -- the `impl_item` table in `tcx` + // isn't populated for such impls. + if impl_trait_ref.references_error() { return; } + + // Locate trait definition and items let tcx = ccx.tcx; + let trait_def = tcx.lookup_trait_def(impl_trait_ref.def_id); let trait_items = tcx.trait_items(impl_trait_ref.def_id); let mut overridden_associated_type = None; @@ -878,6 +939,7 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let ty_trait_item = trait_items.iter() .find(|ac| ac.name() == ty_impl_item.name()); + // Check that impl definition matches trait definition if let Some(ty_trait_item) = ty_trait_item { match impl_item.node { hir::ImplItemKind::Const(..) => { @@ -944,6 +1006,8 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } } + + check_specialization_validity(tcx, trait_def, impl_id, impl_item); } // Check for missing items from trait @@ -952,9 +1016,13 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let mut invalidated_items = Vec::new(); let associated_type_overridden = overridden_associated_type.is_some(); for trait_item in trait_items.iter() { + let is_implemented; + let is_provided; + match *trait_item { ty::ConstTraitItem(ref associated_const) => { - let is_implemented = impl_items.iter().any(|ii| { + is_provided = associated_const.has_value; + is_implemented = impl_items.iter().any(|ii| { match ii.node { hir::ImplItemKind::Const(..) => { ii.name == associated_const.name @@ -962,53 +1030,30 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, _ => false, } }); - let is_provided = associated_const.has_value; - - if !is_implemented { - if !is_provided { - missing_items.push(associated_const.name); - } else if associated_type_overridden { - invalidated_items.push(associated_const.name); - } - } } ty::MethodTraitItem(ref trait_method) => { - let is_implemented = - impl_items.iter().any(|ii| { - match ii.node { - hir::ImplItemKind::Method(..) => { - ii.name == trait_method.name - } - _ => false, - } - }); - let is_provided = - provided_methods.iter().any(|m| m.name == trait_method.name); - if !is_implemented { - if !is_provided { - missing_items.push(trait_method.name); - } else if associated_type_overridden { - invalidated_items.push(trait_method.name); - } - } + is_provided = provided_methods.iter().any(|m| m.name == trait_method.name); + is_implemented = trait_def.ancestors(impl_id) + .fn_defs(tcx, trait_method.name) + .next() + .map(|node_item| !node_item.node.is_from_trait()) + .unwrap_or(false); } - ty::TypeTraitItem(ref associated_type) => { - let is_implemented = impl_items.iter().any(|ii| { - match ii.node { - hir::ImplItemKind::Type(_) => { - ii.name == associated_type.name - } - _ => false, - } - }); - 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); - } - } + ty::TypeTraitItem(ref trait_assoc_ty) => { + is_provided = trait_assoc_ty.ty.is_some(); + is_implemented = trait_def.ancestors(impl_id) + .type_defs(tcx, trait_assoc_ty.name) + .next() + .map(|node_item| !node_item.node.is_from_trait()) + .unwrap_or(false); + } + } + + if !is_implemented { + if !is_provided { + missing_items.push(trait_item.name()); + } else if associated_type_overridden { + invalidated_items.push(trait_item.name()); } } } diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 9dc8d7ae943a1..278d4d8b5b44a 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -15,12 +15,11 @@ // done by the orphan and overlap modules. Then we build up various // mappings. That mapping code resides here. - use middle::def_id::DefId; use middle::lang_items::UnsizeTraitLangItem; use middle::subst::{self, Subst}; -use middle::traits; use middle::ty::{self, TyCtxt, TypeFoldable}; +use middle::traits::{self, ProjectionMode}; use middle::ty::{ImplOrTraitItemId, ConstTraitItemId}; use middle::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment}; use middle::ty::{Ty, TyBool, TyChar, TyEnum, TyError}; @@ -197,7 +196,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { debug!("add_trait_impl: impl_trait_ref={:?} impl_def_id={:?}", impl_trait_ref, impl_def_id); let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id); - trait_def.record_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref); + trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref); } // Converts an implementation in the AST to a vector of items. @@ -385,7 +384,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> { debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)", source, target); - let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env), ProjectionMode::Topmost); let origin = TypeOrigin::Misc(span); let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>, @@ -530,7 +529,10 @@ pub fn report_duplicate_item<'tcx>(tcx: &TyCtxt<'tcx>, sp: Span, name: ast::Name pub fn check_coherence(crate_context: &CrateCtxt) { let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence); - let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None); + let infcx = new_infer_ctxt(crate_context.tcx, + &crate_context.tcx.tables, + None, + ProjectionMode::Topmost); CoherenceChecker { crate_context: crate_context, inference_context: infcx, diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index 80430076f197d..f7fa3e1b1429a 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -12,21 +12,20 @@ //! same type. Likewise, no two inherent impls for a given type //! constructor provide a method with the same name. -use middle::cstore::{CrateStore, LOCAL_CRATE}; +use middle::cstore::CrateStore; use middle::def_id::DefId; -use middle::traits; -use middle::ty::{self, TyCtxt}; +use middle::traits::{self, ProjectionMode}; use middle::infer; +use middle::ty::{self, TyCtxt}; use syntax::ast; use syntax::codemap::Span; use rustc::dep_graph::DepNode; use rustc_front::hir; use rustc_front::intravisit; -use util::nodemap::{DefIdMap, DefIdSet}; +use util::nodemap::DefIdMap; pub fn check(tcx: &TyCtxt) { let mut overlap = OverlapChecker { tcx: tcx, - traits_checked: DefIdSet(), default_impls: DefIdMap() }; // this secondary walk specifically checks for some other cases, @@ -37,134 +36,11 @@ pub fn check(tcx: &TyCtxt) { struct OverlapChecker<'cx, 'tcx:'cx> { tcx: &'cx TyCtxt<'tcx>, - // The set of traits where we have checked for overlap. This is - // used to avoid checking the same trait twice. - // - // NB. It's ok to skip tracking this set because we fully - // encapsulate it, and we always create a task - // (`CoherenceOverlapCheck`) corresponding to each entry. - traits_checked: DefIdSet, - // maps from a trait def-id to an impl id default_impls: DefIdMap, } impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { - fn check_for_overlapping_impls_of_trait(&mut self, trait_def_id: DefId) { - debug!("check_for_overlapping_impls_of_trait(trait_def_id={:?})", - trait_def_id); - - let _task = self.tcx.dep_graph.in_task(DepNode::CoherenceOverlapCheck(trait_def_id)); - if !self.traits_checked.insert(trait_def_id) { - return; - } - - let trait_def = self.tcx.lookup_trait_def(trait_def_id); - self.tcx.populate_implementations_for_trait_if_necessary( - trait_def.trait_ref.def_id); - - // We should already know all impls of this trait, so these - // borrows are safe. - let (blanket_impls, nonblanket_impls) = trait_def.borrow_impl_lists(self.tcx); - - // Conflicts can only occur between a blanket impl and another impl, - // or between 2 non-blanket impls of the same kind. - - for (i, &impl1_def_id) in blanket_impls.iter().enumerate() { - for &impl2_def_id in &blanket_impls[(i+1)..] { - self.check_if_impls_overlap(impl1_def_id, - impl2_def_id); - } - - for v in nonblanket_impls.values() { - for &impl2_def_id in v { - self.check_if_impls_overlap(impl1_def_id, - impl2_def_id); - } - } - } - - for impl_group in nonblanket_impls.values() { - for (i, &impl1_def_id) in impl_group.iter().enumerate() { - for &impl2_def_id in &impl_group[(i+1)..] { - self.check_if_impls_overlap(impl1_def_id, - impl2_def_id); - } - } - } - } - - // We need to coherently pick which impl will be displayed - // as causing the error message, and it must be the in the current - // crate. Just pick the smaller impl in the file. - fn order_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) - -> Option<(DefId, DefId)> { - if impl1_def_id.krate != LOCAL_CRATE { - if impl2_def_id.krate != LOCAL_CRATE { - // we don't need to check impls if both are external; - // that's the other crate's job. - None - } else { - Some((impl2_def_id, impl1_def_id)) - } - } else if impl2_def_id.krate != LOCAL_CRATE { - Some((impl1_def_id, impl2_def_id)) - } else if impl1_def_id < impl2_def_id { - Some((impl1_def_id, impl2_def_id)) - } else { - Some((impl2_def_id, impl1_def_id)) - } - } - - fn check_if_impls_overlap(&self, - impl1_def_id: DefId, - impl2_def_id: DefId) - { - if let Some((impl1_def_id, impl2_def_id)) = self.order_impls( - impl1_def_id, impl2_def_id) - { - debug!("check_if_impls_overlap({:?}, {:?})", - impl1_def_id, - impl2_def_id); - - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None); - if let Some(header) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) { - self.report_overlap_error(impl1_def_id, impl2_def_id, header.trait_ref.unwrap()); - } - } - } - - fn report_overlap_error(&self, - impl1: DefId, - impl2: DefId, - trait_ref: ty::TraitRef) - { - // only print the Self type if it's concrete; otherwise, it's not adding much information. - let self_type = { - trait_ref.substs.self_ty().and_then(|ty| { - if let ty::TyInfer(_) = ty.sty { - None - } else { - Some(format!(" for type `{}`", ty)) - } - }).unwrap_or(String::new()) - }; - - let mut err = struct_span_err!(self.tcx.sess, self.span_of_def_id(impl1), E0119, - "conflicting implementations of trait `{}`{}:", - trait_ref, - self_type); - - if impl2.is_local() { - span_note!(&mut err, self.span_of_def_id(impl2), - "conflicting implementation is here:"); - } else { - let cname = self.tcx.sess.cstore.crate_name(impl2.krate); - err.note(&format!("conflicting implementation in crate `{}`", cname)); - } - err.emit(); - } - fn span_of_def_id(&self, did: DefId) -> Span { let node_id = self.tcx.map.as_local_node_id(did).unwrap(); self.tcx.map.span(node_id) @@ -213,7 +89,10 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { for (i, &impl1_def_id) in impls.iter().enumerate() { for &impl2_def_id in &impls[(i+1)..] { - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None); + let infcx = infer::new_infer_ctxt(self.tcx, + &self.tcx.tables, + None, + ProjectionMode::Topmost); if traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id).is_some() { self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id) } @@ -222,15 +101,9 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { } } - impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { fn visit_item(&mut self, item: &'v hir::Item) { match item.node { - hir::ItemTrait(..) => { - let trait_def_id = self.tcx.map.local_def_id(item.id); - self.check_for_overlapping_impls_of_trait(trait_def_id); - } - hir::ItemEnum(..) | hir::ItemStruct(..) => { let type_def_id = self.tcx.map.local_def_id(item.id); self.check_for_overlapping_inherent_impls(type_def_id); @@ -243,50 +116,90 @@ impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { let impl_def_id = self.tcx.map.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); - self.check_for_overlapping_impls_of_trait(trait_ref.def_id); - let prev_default_impl = self.default_impls.insert(trait_ref.def_id, item.id); - match prev_default_impl { - Some(prev_id) => { - self.report_overlap_error(impl_def_id, - self.tcx.map.local_def_id(prev_id), - trait_ref); - } - None => { } + if let Some(prev_id) = prev_default_impl { + let mut err = struct_span_err!( + self.tcx.sess, + self.tcx.span_of_impl(impl_def_id).unwrap(), E0521, + "redundant default implementations of trait `{}`:", + trait_ref); + err.span_note(self.tcx.span_of_impl(self.tcx.map.local_def_id(prev_id)) + .unwrap(), + "redundant implementation is here:"); + err.emit(); } } hir::ItemImpl(_, _, _, Some(_), _, _) => { let impl_def_id = self.tcx.map.local_def_id(item.id); let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_def_id = trait_ref.def_id; - self.check_for_overlapping_impls_of_trait(trait_def_id); - match trait_ref.self_ty().sty { - ty::TyTrait(ref data) => { - // This is something like impl Trait1 for Trait2. Illegal - // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. - if !traits::is_object_safe(self.tcx, data.principal_def_id()) { - // This is an error, but it will be - // reported by wfcheck. Ignore it - // here. This is tested by - // `coherence-impl-trait-for-trait-object-safe.rs`. - } else { - let mut supertrait_def_ids = - traits::supertrait_def_ids(self.tcx, data.principal_def_id()); - if supertrait_def_ids.any(|d| d == trait_def_id) { - span_err!(self.tcx.sess, item.span, E0371, - "the object type `{}` automatically \ - implements the trait `{}`", - trait_ref.self_ty(), - self.tcx.item_path_str(trait_def_id)); + let _task = self.tcx.dep_graph.in_task( + DepNode::CoherenceOverlapCheck(trait_def_id)); + + let def = self.tcx.lookup_trait_def(trait_def_id); + + // attempt to insert into the specialization graph + let insert_result = def.add_impl_for_specialization(self.tcx, impl_def_id); + + // insertion failed due to overlap + if let Err(overlap) = insert_result { + // only print the Self type if it has at least some outer + // concrete shell; otherwise, it's not adding much + // information. + let self_type = { + overlap.on_trait_ref.substs.self_ty().and_then(|ty| { + if ty.has_concrete_skeleton() { + Some(format!(" for type `{}`", ty)) + } else { + None } + }).unwrap_or(String::new()) + }; + + let mut err = struct_span_err!( + self.tcx.sess, self.tcx.span_of_impl(impl_def_id).unwrap(), E0119, + "conflicting implementations of trait `{}`{}:", + overlap.on_trait_ref, + self_type); + + match self.tcx.span_of_impl(overlap.with_impl) { + Ok(span) => { + err.span_note(span, "conflicting implementation is here:"); + } + Err(cname) => { + err.note(&format!("conflicting implementation in crate `{}`", + cname)); + } + } + + err.emit(); + } + + // check for overlap with the automatic `impl Trait for Trait` + if let ty::TyTrait(ref data) = trait_ref.self_ty().sty { + // This is something like impl Trait1 for Trait2. Illegal + // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. + + if !traits::is_object_safe(self.tcx, data.principal_def_id()) { + // This is an error, but it will be + // reported by wfcheck. Ignore it + // here. This is tested by + // `coherence-impl-trait-for-trait-object-safe.rs`. + } else { + let mut supertrait_def_ids = + traits::supertrait_def_ids(self.tcx, data.principal_def_id()); + if supertrait_def_ids.any(|d| d == trait_def_id) { + span_err!(self.tcx.sess, item.span, E0371, + "the object type `{}` automatically \ + implements the trait `{}`", + trait_ref.self_ty(), + self.tcx.item_path_str(trait_def_id)); } } - _ => { } } } - _ => { - } + _ => {} } } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b97d3f4993a40..0f88640b62951 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -533,6 +533,7 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, id: ast::NodeId, vis: hir::Visibility, sig: &hir::MethodSig, + defaultness: hir::Defaultness, untransformed_rcvr_ty: Ty<'tcx>, rcvr_ty_generics: &ty::Generics<'tcx>, rcvr_ty_predicates: &ty::GenericPredicates<'tcx>) { @@ -554,6 +555,7 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fty, explicit_self_category, vis, + defaultness, def_id, container); @@ -600,6 +602,7 @@ fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, name: ast::Name, id: ast::NodeId, vis: hir::Visibility, + defaultness: hir::Defaultness, ty: ty::Ty<'tcx>, has_value: bool) { @@ -611,6 +614,7 @@ fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let associated_const = Rc::new(ty::AssociatedConst { name: name, vis: vis, + defaultness: defaultness, def_id: ccx.tcx.map.local_def_id(id), container: container, ty: ty, @@ -625,11 +629,13 @@ fn convert_associated_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, name: ast::Name, id: ast::NodeId, vis: hir::Visibility, + defaultness: hir::Defaultness, ty: Option>) { let associated_type = Rc::new(ty::AssociatedType { name: name, vis: vis, + defaultness: defaultness, ty: ty, def_id: ccx.tcx.map.local_def_id(id), container: container @@ -767,6 +773,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { convert_associated_const(ccx, ImplContainer(def_id), impl_item.name, impl_item.id, impl_item.vis.inherit_from(parent_visibility), + impl_item.defaultness, ty, true /* has_value */); } } @@ -783,7 +790,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { convert_associated_type(ccx, ImplContainer(def_id), impl_item.name, impl_item.id, impl_item.vis, - Some(typ)); + impl_item.defaultness, Some(typ)); } } @@ -797,7 +804,8 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { convert_method(ccx, ImplContainer(def_id), impl_item.name, impl_item.id, method_vis, - sig, selfty, &ty_generics, &ty_predicates); + sig, impl_item.defaultness, selfty, &ty_generics, + &ty_predicates); } } @@ -831,6 +839,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { trait_item.name, trait_item.id, hir::Public, + hir::Defaultness::Default, ty, default.is_some()) } @@ -848,6 +857,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { trait_item.name, trait_item.id, hir::Public, + hir::Defaultness::Default, typ); } } @@ -861,6 +871,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { trait_item.id, hir::Inherited, sig, + hir::Defaultness::Default, tcx.mk_self_type(), &trait_def.generics, &trait_predicates); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 5c411bec5065d..19cfc13ea615c 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3695,5 +3695,7 @@ register_diagnostics! { E0399, // trait items need to be implemented because the associated // type `{}` was overridden E0436, // functional record update requires a struct - E0513 // no type for local variable .. + E0513, // no type for local variable .. + E0520, // cannot specialize non-default item + E0521 // redundant default implementations of trait } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 125c3d426a803..936be80919406 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -106,6 +106,7 @@ use middle::def::Def; use middle::infer::{self, TypeOrigin}; use middle::subst::Substs; use middle::ty::{self, Ty, TyCtxt, TypeFoldable}; +use middle::traits::ProjectionMode; use session::{config, CompileResult}; use util::common::time; use rustc_front::hir; @@ -196,7 +197,7 @@ fn require_same_types<'a, 'tcx, M>(tcx: &TyCtxt<'tcx>, { let result = match maybe_infcx { None => { - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, ProjectionMode::AnyFinal); infer::mk_eqty(&infcx, t1_is_expected, TypeOrigin::Misc(span), t1, t2) } Some(infcx) => { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index f7621b0131ad4..c830fed5db9a7 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1328,10 +1328,10 @@ pub struct MethodSig { pub explicit_self: ExplicitSelf, } -/// Represents a method declaration in a trait declaration, possibly including -/// a default implementation. A trait method is either required (meaning it -/// doesn't have an implementation, just a signature) or provided (meaning it -/// has a default implementation). +/// Represents an item declaration within a trait declaration, +/// possibly including a default implementation. A trait item is +/// either required (meaning it doesn't have an implementation, just a +/// signature) or provided (meaning it has a default implementation). #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct TraitItem { pub id: NodeId, @@ -1353,6 +1353,7 @@ pub struct ImplItem { pub id: NodeId, pub ident: Ident, pub vis: Visibility, + pub defaultness: Defaultness, pub attrs: Vec, pub node: ImplItemKind, pub span: Span, @@ -1654,6 +1655,12 @@ pub enum Constness { NotConst, } +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum Defaultness { + Default, + Final, +} + impl fmt::Display for Unsafety { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(match *self { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f5794f7219bcf..5bfdab791d638 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1061,6 +1061,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) ident: ii.ident, attrs: ii.attrs, vis: ii.vis, + defaultness: ii.defaultness, node: match ii.node { ast::ImplItemKind::Method(sig, body) => { let (sig, body) = expand_and_rename_method(sig, body, fld); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 14a3f93738a32..fbaf28332c42c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -247,7 +247,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status ("inclusive_range_syntax", "1.7.0", Some(28237), Active), // `expr?` - ("question_mark", "1.9.0", Some(31436), Active) + ("question_mark", "1.9.0", Some(31436), Active), + + // impl specialization (RFC 1210) + ("specialization", "1.7.0", Some(31844), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -574,6 +577,7 @@ pub struct Features { pub stmt_expr_attributes: bool, pub deprecated: bool, pub question_mark: bool, + pub specialization: bool, } impl Features { @@ -608,6 +612,7 @@ impl Features { stmt_expr_attributes: false, deprecated: false, question_mark: false, + specialization: false, } } } @@ -1102,6 +1107,12 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { } fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { + if ii.defaultness == ast::Defaultness::Default { + self.gate_feature("specialization", + ii.span, + "specialization is unstable"); + } + match ii.node { ast::ImplItemKind::Const(..) => { self.gate_feature("associated_consts", @@ -1212,6 +1223,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &Handler, stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"), deprecated: cx.has_feature("deprecated"), question_mark: cx.has_feature("question_mark"), + specialization: cx.has_feature("specialization"), } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9056103d30086..cd8998a211ae7 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -993,6 +993,7 @@ pub fn noop_fold_impl_item(i: ImplItem, folder: &mut T) ident: folder.fold_ident(i.ident), attrs: fold_attrs(i.attrs, folder), vis: i.vis, + defaultness: i.defaultness, node: match i.node { ast::ImplItemKind::Const(ty, expr) => { ast::ImplItemKind::Const(folder.fold_ty(ty), folder.fold_expr(expr)) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 5884be401503d..6839f11cd709d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -18,7 +18,7 @@ use ast::{Mod, Arg, Arm, Attribute, BindingMode, TraitItemKind}; use ast::Block; use ast::{BlockCheckMode, CaptureBy}; use ast::{Constness, Crate, CrateConfig}; -use ast::{Decl, DeclKind}; +use ast::{Decl, DeclKind, Defaultness}; use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{Expr, ExprKind, RangeLimits}; use ast::{Field, FnDecl}; @@ -644,6 +644,25 @@ impl<'a> Parser<'a> { } } + pub fn check_contextual_keyword(&mut self, ident: Ident) -> bool { + let tok = token::Ident(ident, token::Plain); + self.expected_tokens.push(TokenType::Token(tok)); + if let token::Ident(ref cur_ident, _) = self.token { + cur_ident.name == ident.name + } else { + false + } + } + + pub fn eat_contextual_keyword(&mut self, ident: Ident) -> bool { + if self.check_contextual_keyword(ident) { + self.bump(); + true + } else { + false + } + } + /// If the given word is not a keyword, signal an error. /// If the next token is not the given word, signal an error. /// Otherwise, eat it. @@ -705,7 +724,6 @@ impl<'a> Parser<'a> { } } - /// Attempt to consume a `<`. If `<<` is seen, replace it with a single /// `<` and continue. If a `<` is not seen, return false. /// @@ -4846,6 +4864,7 @@ impl<'a> Parser<'a> { let mut attrs = try!(self.parse_outer_attributes()); let lo = self.span.lo; let vis = try!(self.parse_visibility()); + let defaultness = try!(self.parse_defaultness()); let (name, node) = if self.eat_keyword(keywords::Type) { let name = try!(self.parse_ident()); try!(self.expect(&token::Eq)); @@ -4872,6 +4891,7 @@ impl<'a> Parser<'a> { span: mk_sp(lo, self.last_span.hi), ident: name, vis: vis, + defaultness: defaultness, attrs: attrs, node: node }) @@ -5208,6 +5228,15 @@ impl<'a> Parser<'a> { else { Ok(Visibility::Inherited) } } + /// Parse defaultness: DEFAULT or nothing + fn parse_defaultness(&mut self) -> PResult<'a, Defaultness> { + if self.eat_contextual_keyword(special_idents::DEFAULT) { + Ok(Defaultness::Default) + } else { + Ok(Defaultness::Final) + } + } + /// Given a termination token, parse all of the items in a module fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<'a, Mod> { let mut items = vec![]; diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 294cbf358954f..033ac9440bcec 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -545,66 +545,67 @@ declare_special_idents_and_keywords! { (9, __unused1, "<__unused1>"); (super::SELF_TYPE_KEYWORD_NAME_NUM, type_self, "Self"); (11, prelude_import, "prelude_import"); + (12, DEFAULT, "default"); } pub mod keywords { // These ones are variants of the Keyword enum 'strict: - (12, As, "as"); - (13, Break, "break"); - (14, Crate, "crate"); - (15, Else, "else"); - (16, Enum, "enum"); - (17, Extern, "extern"); - (18, False, "false"); - (19, Fn, "fn"); - (20, For, "for"); - (21, If, "if"); - (22, Impl, "impl"); - (23, In, "in"); - (24, Let, "let"); - (25, Loop, "loop"); - (26, Match, "match"); - (27, Mod, "mod"); - (28, Move, "move"); - (29, Mut, "mut"); - (30, Pub, "pub"); - (31, Ref, "ref"); - (32, Return, "return"); + (13, As, "as"); + (14, Break, "break"); + (15, Crate, "crate"); + (16, Else, "else"); + (17, Enum, "enum"); + (18, Extern, "extern"); + (19, False, "false"); + (20, Fn, "fn"); + (21, For, "for"); + (22, If, "if"); + (23, Impl, "impl"); + (24, In, "in"); + (25, Let, "let"); + (26, Loop, "loop"); + (27, Match, "match"); + (28, Mod, "mod"); + (29, Move, "move"); + (30, Mut, "mut"); + (31, Pub, "pub"); + (32, Ref, "ref"); + (33, Return, "return"); // Static and Self are also special idents (prefill de-dupes) (super::STATIC_KEYWORD_NAME_NUM, Static, "static"); (super::SELF_KEYWORD_NAME_NUM, SelfValue, "self"); (super::SELF_TYPE_KEYWORD_NAME_NUM, SelfType, "Self"); - (33, Struct, "struct"); + (34, Struct, "struct"); (super::SUPER_KEYWORD_NAME_NUM, Super, "super"); - (34, True, "true"); - (35, Trait, "trait"); - (36, Type, "type"); - (37, Unsafe, "unsafe"); - (38, Use, "use"); - (39, While, "while"); - (40, Continue, "continue"); - (41, Box, "box"); - (42, Const, "const"); - (43, Where, "where"); + (35, True, "true"); + (36, Trait, "trait"); + (37, Type, "type"); + (38, Unsafe, "unsafe"); + (39, Use, "use"); + (40, While, "while"); + (41, Continue, "continue"); + (42, Box, "box"); + (43, Const, "const"); + (44, Where, "where"); 'reserved: - (44, Virtual, "virtual"); - (45, Proc, "proc"); - (46, Alignof, "alignof"); - (47, Become, "become"); - (48, Offsetof, "offsetof"); - (49, Priv, "priv"); - (50, Pure, "pure"); - (51, Sizeof, "sizeof"); - (52, Typeof, "typeof"); - (53, Unsized, "unsized"); - (54, Yield, "yield"); - (55, Do, "do"); - (56, Abstract, "abstract"); - (57, Final, "final"); - (58, Override, "override"); - (59, Macro, "macro"); + (45, Virtual, "virtual"); + (46, Proc, "proc"); + (47, Alignof, "alignof"); + (48, Become, "become"); + (49, Offsetof, "offsetof"); + (50, Priv, "priv"); + (51, Pure, "pure"); + (52, Sizeof, "sizeof"); + (53, Typeof, "typeof"); + (54, Unsized, "unsized"); + (55, Yield, "yield"); + (56, Do, "do"); + (57, Abstract, "abstract"); + (58, Final, "final"); + (59, Override, "override"); + (60, Macro, "macro"); } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 2cfed1f82f7ec..533487ae1c547 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1582,6 +1582,9 @@ impl<'a> State<'a> { try!(self.hardbreak_if_not_bol()); try!(self.maybe_print_comment(ii.span.lo)); try!(self.print_outer_attributes(&ii.attrs)); + if let ast::Defaultness::Default = ii.defaultness { + try!(self.word_nbsp("default")); + } match ii.node { ast::ImplItemKind::Const(ref ty, ref expr) => { try!(self.print_associated_const(ii.ident, &ty, Some(&expr), ii.vis)); diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index c0237a5d29a41..937055fcfa672 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -476,6 +476,7 @@ impl<'a> TraitDef<'a> { span: self.span, ident: ident, vis: ast::Visibility::Inherited, + defaultness: ast::Defaultness::Final, attrs: Vec::new(), node: ast::ImplItemKind::Type(type_def.to_ty(cx, self.span, @@ -893,6 +894,7 @@ impl<'a> MethodDef<'a> { attrs: self.attributes.clone(), span: trait_.span, vis: ast::Visibility::Inherited, + defaultness: ast::Defaultness::Final, ident: method_ident, node: ast::ImplItemKind::Method(ast::MethodSig { generics: fn_generics, diff --git a/src/test/auxiliary/go_trait.rs b/src/test/auxiliary/go_trait.rs index 0a921c8f5b3a0..044bb606b40e2 100644 --- a/src/test/auxiliary/go_trait.rs +++ b/src/test/auxiliary/go_trait.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(specialization)] + // Common code used for tests that model the Fn/FnMut/FnOnce hierarchy. pub trait Go { @@ -37,7 +39,7 @@ pub fn go_once(this: G, arg: isize) { impl GoMut for G where G : Go { - fn go_mut(&mut self, arg: isize) { + default fn go_mut(&mut self, arg: isize) { go(&*self, arg) } } @@ -45,7 +47,7 @@ impl GoMut for G impl GoOnce for G where G : GoMut { - fn go_once(mut self, arg: isize) { + default fn go_once(mut self, arg: isize) { go_mut(&mut self, arg) } } diff --git a/src/test/auxiliary/specialization_cross_crate.rs b/src/test/auxiliary/specialization_cross_crate.rs new file mode 100644 index 0000000000000..1d235336de821 --- /dev/null +++ b/src/test/auxiliary/specialization_cross_crate.rs @@ -0,0 +1,82 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +pub trait Foo { + fn foo(&self) -> &'static str; +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic" + } +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone" + } +} + +impl Foo for (T, U) where T: Clone, U: Clone { + default fn foo(&self) -> &'static str { + "generic pair" + } +} + +impl Foo for (T, T) { + default fn foo(&self) -> &'static str { + "generic uniform pair" + } +} + +impl Foo for (u8, u32) { + default fn foo(&self) -> &'static str { + "(u8, u32)" + } +} + +impl Foo for (u8, u8) { + default fn foo(&self) -> &'static str { + "(u8, u8)" + } +} + +impl Foo for Vec { + default fn foo(&self) -> &'static str { + "generic Vec" + } +} + +impl Foo for Vec { + fn foo(&self) -> &'static str { + "Vec" + } +} + +impl Foo for String { + fn foo(&self) -> &'static str { + "String" + } +} + +impl Foo for i32 { + fn foo(&self) -> &'static str { + "i32" + } +} + +pub trait MyMarker {} +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone + MyMarker" + } +} diff --git a/src/test/auxiliary/specialization_cross_crate_defaults.rs b/src/test/auxiliary/specialization_cross_crate_defaults.rs new file mode 100755 index 0000000000000..b62d80b589fda --- /dev/null +++ b/src/test/auxiliary/specialization_cross_crate_defaults.rs @@ -0,0 +1,49 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +#![feature(specialization)] + +// First, test only use of explicit `default` items: + +pub trait Foo { + fn foo(&self) -> bool; +} + +impl Foo for T { + default fn foo(&self) -> bool { false } +} + +impl Foo for i32 {} + +impl Foo for i64 { + fn foo(&self) -> bool { true } +} + +// Next, test mixture of explicit `default` and provided methods: + +pub trait Bar { + fn bar(&self) -> i32 { 0 } +} + +impl Bar for T {} // use the provided method + +impl Bar for i32 { + fn bar(&self) -> i32 { 1 } +} +impl<'a> Bar for &'a str {} + +impl Bar for Vec { + default fn bar(&self) -> i32 { 2 } +} +impl Bar for Vec {} +impl Bar for Vec { + fn bar(&self) -> i32 { 3 } +} diff --git a/src/test/auxiliary/xcrate_associated_type_defaults.rs b/src/test/auxiliary/xcrate_associated_type_defaults.rs index 43852a4e793f3..6779438c67226 100644 --- a/src/test/auxiliary/xcrate_associated_type_defaults.rs +++ b/src/test/auxiliary/xcrate_associated_type_defaults.rs @@ -10,9 +10,13 @@ #![feature(associated_type_defaults)] -pub trait Foo { - type Input = usize; - fn bar(&self, _: Self::Input) {} +pub trait Foo { + type Out: Default + ToString = T; } -impl Foo for () {} +impl Foo for () { +} + +impl Foo for () { + type Out = bool; +} diff --git a/src/test/compile-fail/associated-types-coherence-failure.rs b/src/test/compile-fail/associated-types-coherence-failure.rs index 6d68da54112f2..786a25500a886 100644 --- a/src/test/compile-fail/associated-types-coherence-failure.rs +++ b/src/test/compile-fail/associated-types-coherence-failure.rs @@ -22,21 +22,21 @@ pub trait IntoCow<'a, B: ?Sized> { fn into_cow(self) -> Cow<'a, B>; } -impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned { -//~^ ERROR E0119 +impl<'a, B: ?Sized> IntoCow<'a, B> for ::Owned where B: ToOwned { fn into_cow(self) -> Cow<'a, B> { - self + Cow(PhantomData) } } -impl<'a, B: ?Sized> IntoCow<'a, B> for ::Owned where B: ToOwned { +impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned { //~^ ERROR E0119 fn into_cow(self) -> Cow<'a, B> { - Cow(PhantomData) + self } } impl<'a, B: ?Sized> IntoCow<'a, B> for &'a B where B: ToOwned { +//~^ ERROR E0119 fn into_cow(self) -> Cow<'a, B> { Cow(PhantomData) } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs index b771b959d3e50..434d77828b44a 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-implemented.rs @@ -27,11 +27,11 @@ impl Even for isize { } impl Odd for usize { } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } -impl MyTrait for T { +impl MyTrait for T { //~ ERROR E0119 fn get(&self) -> usize { 0 } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs index d3b0e7f10b91b..7ad5cd71ca8c2 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-blanket-unimplemented.rs @@ -23,11 +23,11 @@ trait Even {} trait Odd {} -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } -impl MyTrait for T { +impl MyTrait for T { //~ ERROR E0119 fn get(&self) -> usize { 0 } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs index 7b60a5ecbd71f..1defe6c8b20e3 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-multidispatch.rs @@ -18,7 +18,7 @@ trait MyTrait { fn get(&self) -> T; } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> T { panic!() } @@ -29,7 +29,7 @@ struct MyType { dummy: usize } -impl MyTrait for MyType { +impl MyTrait for MyType { //~ ERROR E0119 fn get(&self) -> usize { (*self).clone() } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs index eeaa68677eb67..5c5c4d32d675c 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific-trait.rs @@ -19,7 +19,7 @@ trait MyTrait { fn get(&self) -> usize; } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } @@ -27,7 +27,7 @@ struct MyType { dummy: usize } -impl MyTrait for MyType { +impl MyTrait for MyType { //~ ERROR E0119 fn get(&self) -> usize { self.dummy } } diff --git a/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs index d218b64af0527..57d71b44b0f6b 100644 --- a/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs +++ b/src/test/compile-fail/coherence-blanket-conflicts-with-specific.rs @@ -18,7 +18,7 @@ trait MyTrait { fn get(&self) -> usize; } -impl MyTrait for T { //~ ERROR E0119 +impl MyTrait for T { fn get(&self) -> usize { 0 } } @@ -26,7 +26,7 @@ struct MyType { dummy: usize } -impl MyTrait for MyType { +impl MyTrait for MyType { //~ ERROR E0119 fn get(&self) -> usize { self.dummy } } diff --git a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs index 344ec89d25de9..c123e381ab7d7 100644 --- a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs +++ b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs @@ -15,8 +15,6 @@ trait MyTrait {} struct TestType(::std::marker::PhantomData); unsafe impl Send for TestType {} -//~^ ERROR conflicting implementations of trait `core::marker::Send` -//~^^ ERROR conflicting implementations of trait `core::marker::Send` impl !Send for TestType {} //~^ ERROR conflicting implementations of trait `core::marker::Send` diff --git a/src/test/compile-fail/coherence-cross-crate-conflict.rs b/src/test/compile-fail/coherence-cross-crate-conflict.rs index a020b518d8273..9f74afbb2b3b5 100644 --- a/src/test/compile-fail/coherence-cross-crate-conflict.rs +++ b/src/test/compile-fail/coherence-cross-crate-conflict.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Regression test for #3512 - conflicting trait impls in different crates should give a -// 'conflicting implementations' error message. +// The error here is strictly due to orphan rules; the impl here +// generalizes the one upstream // aux-build:trait_impl_conflict.rs extern crate trait_impl_conflict; @@ -17,7 +17,6 @@ use trait_impl_conflict::Foo; impl Foo for A { //~^ ERROR type parameter `A` must be used as the type parameter for some local type - //~^^ ERROR E0119 } fn main() { diff --git a/src/test/compile-fail/coherence-default-trait-impl.rs b/src/test/compile-fail/coherence-default-trait-impl.rs index 0705702b031ee..3d109de76ccd1 100644 --- a/src/test/compile-fail/coherence-default-trait-impl.rs +++ b/src/test/compile-fail/coherence-default-trait-impl.rs @@ -15,7 +15,7 @@ trait MyTrait {} impl MyTrait for .. {} impl MyTrait for .. {} -//~^ ERROR conflicting implementations of trait `MyTrait` +//~^ ERROR redundant default implementations of trait `MyTrait` trait MySafeTrait {} diff --git a/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs b/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs new file mode 100644 index 0000000000000..6de338f1db0fa --- /dev/null +++ b/src/test/compile-fail/coherence-no-direct-lifetime-dispatch.rs @@ -0,0 +1,18 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that you cannot *directly* dispatch on lifetime requirements + +trait MyTrait {} + +impl MyTrait for T {} +impl MyTrait for T {} //~ ERROR E0119 + +fn main() {} diff --git a/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs index 3fd635b3d616f..928ba7a36db26 100644 --- a/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs +++ b/src/test/compile-fail/coherence-overlap-all-t-and-tuple.rs @@ -19,10 +19,10 @@ trait From { } -impl From for T { //~ ERROR E0119 +impl From for T { } -impl From<(U11,)> for (T11,) { +impl From<(U11,)> for (T11,) { //~ ERROR E0119 } fn main() { } diff --git a/src/test/compile-fail/coherence-overlap-issue-23516.rs b/src/test/compile-fail/coherence-overlap-issue-23516.rs index d7f060a3bfe73..51d7c3e8b4cb1 100644 --- a/src/test/compile-fail/coherence-overlap-issue-23516.rs +++ b/src/test/compile-fail/coherence-overlap-issue-23516.rs @@ -14,6 +14,6 @@ pub trait Sugar { fn dummy(&self) { } } pub trait Sweet { fn dummy(&self) { } } -impl Sweet for T { } //~ ERROR E0119 -impl Sweet for Box { } +impl Sweet for T { } +impl Sweet for Box { } //~ ERROR E0119 fn main() { } diff --git a/src/test/compile-fail/coherence-overlap-messages.rs b/src/test/compile-fail/coherence-overlap-messages.rs index 4f1092f960e0d..0ae8135221c21 100644 --- a/src/test/compile-fail/coherence-overlap-messages.rs +++ b/src/test/compile-fail/coherence-overlap-messages.rs @@ -10,33 +10,23 @@ trait Foo {} -impl Foo for T {} //~ ERROR conflicting implementations of trait `Foo`: -impl Foo for U {} +impl Foo for T {} +impl Foo for U {} //~ ERROR conflicting implementations of trait `Foo`: trait Bar {} -impl Bar for T {} //~ ERROR conflicting implementations of trait `Bar` for type `u8`: -impl Bar for u8 {} +impl Bar for (T, u8) {} +impl Bar for (u8, T) {} //~ ERROR conflicting implementations of trait `Bar` for type `(u8, u8)`: trait Baz {} -impl Baz for T {} //~ ERROR conflicting implementations of trait `Baz<_>` for type `u8`: -impl Baz for u8 {} +impl Baz for T {} +impl Baz for u8 {} //~ ERROR conflicting implementations of trait `Baz` for type `u8`: -trait Quux {} +trait Quux {} -impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_>`: -impl Quux for T {} - -trait Qaar {} - -impl Qaar for T {} //~ ERROR conflicting implementations of trait `Qaar`: -impl Qaar for T {} - -trait Qaax {} - -impl Qaax for T {} -//~^ ERROR conflicting implementations of trait `Qaax` for type `u32`: -impl Qaax for u32 {} +impl Quux for T {} +impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`: +impl Quux for T {} //~ ERROR conflicting implementations of trait `Quux<_, _>`: fn main() {} diff --git a/src/test/compile-fail/coherence-projection-conflict-orphan.rs b/src/test/compile-fail/coherence-projection-conflict-orphan.rs index 3de7945439838..3ed3549de89aa 100644 --- a/src/test/compile-fail/coherence-projection-conflict-orphan.rs +++ b/src/test/compile-fail/coherence-projection-conflict-orphan.rs @@ -21,8 +21,8 @@ pub trait Bar { type Output: 'static; } -impl Foo for i32 { } //~ ERROR E0119 +impl Foo for i32 { } -impl Foo for A { } +impl Foo for A { } //~ ERROR E0119 fn main() {} diff --git a/src/test/compile-fail/coherence-projection-conflict-ty-param.rs b/src/test/compile-fail/coherence-projection-conflict-ty-param.rs index 6880f3e9a3cc9..f04902a70f68c 100644 --- a/src/test/compile-fail/coherence-projection-conflict-ty-param.rs +++ b/src/test/compile-fail/coherence-projection-conflict-ty-param.rs @@ -15,8 +15,8 @@ use std::marker::PhantomData; pub trait Foo

{} -impl > Foo

for Option {} //~ ERROR E0119 +impl > Foo

for Option {} -impl Foo for Option { } +impl Foo for Option { } //~ ERROR E0119 fn main() {} diff --git a/src/test/compile-fail/coherence-projection-conflict.rs b/src/test/compile-fail/coherence-projection-conflict.rs index 2236e71b53fff..6d3ab32f06f43 100644 --- a/src/test/compile-fail/coherence-projection-conflict.rs +++ b/src/test/compile-fail/coherence-projection-conflict.rs @@ -16,9 +16,9 @@ pub trait Bar { type Output: 'static; } -impl Foo for i32 { } //~ ERROR E0119 +impl Foo for i32 { } -impl Foo for A { } +impl Foo for A { } //~ ERROR E0119 impl Bar for i32 { type Output = i32; diff --git a/src/test/compile-fail/coherence-tuple-conflict.rs b/src/test/compile-fail/coherence-tuple-conflict.rs index 87b007fdd6982..7807f93df1a67 100644 --- a/src/test/compile-fail/coherence-tuple-conflict.rs +++ b/src/test/compile-fail/coherence-tuple-conflict.rs @@ -18,11 +18,11 @@ trait MyTrait { fn get(&self) -> usize; } -impl MyTrait for (T,T) { //~ ERROR E0119 +impl MyTrait for (T,T) { fn get(&self) -> usize { 0 } } -impl MyTrait for (A,B) { +impl MyTrait for (A,B) { //~ ERROR E0119 fn get(&self) -> usize { self.dummy } } diff --git a/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs b/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs index a6b62d17bc4e6..8e3e3f31cb5f1 100644 --- a/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs +++ b/src/test/compile-fail/coherence_copy_like_err_fundamental_struct_tuple.rs @@ -21,10 +21,10 @@ struct MyType { x: i32 } trait MyTrait { } -impl MyTrait for T { } //~ ERROR E0119 +impl MyTrait for T { } // Tuples are not fundamental. -impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { } +impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { } //~ ERROR E0119 #[rustc_error] fn main() { } diff --git a/src/test/compile-fail/coherence_copy_like_err_struct.rs b/src/test/compile-fail/coherence_copy_like_err_struct.rs index 5a9f440f8bb6a..35bc17b8e8870 100644 --- a/src/test/compile-fail/coherence_copy_like_err_struct.rs +++ b/src/test/compile-fail/coherence_copy_like_err_struct.rs @@ -18,7 +18,7 @@ extern crate coherence_copy_like_lib as lib; struct MyType { x: i32 } trait MyTrait { } -impl MyTrait for T { } //~ ERROR E0119 +impl MyTrait for T { } // `MyStruct` is not declared fundamental, therefore this would // require that @@ -26,6 +26,6 @@ impl MyTrait for T { } //~ ERROR E0119 // MyStruct: !MyTrait // // which we cannot approve. -impl MyTrait for lib::MyStruct { } +impl MyTrait for lib::MyStruct { } //~ ERROR E0119 fn main() { } diff --git a/src/test/compile-fail/coherence_copy_like_err_tuple.rs b/src/test/compile-fail/coherence_copy_like_err_tuple.rs index ee0d5550fd61f..a70cc92955fb0 100644 --- a/src/test/compile-fail/coherence_copy_like_err_tuple.rs +++ b/src/test/compile-fail/coherence_copy_like_err_tuple.rs @@ -18,13 +18,13 @@ extern crate coherence_copy_like_lib as lib; struct MyType { x: i32 } trait MyTrait { } -impl MyTrait for T { } //~ ERROR E0119 +impl MyTrait for T { } // Tuples are not fundamental, therefore this would require that // // (MyType,): !MyTrait // // which we cannot approve. -impl MyTrait for (MyType,) { } +impl MyTrait for (MyType,) { } //~ ERROR E0119 fn main() { } diff --git a/src/test/compile-fail/issue-28568.rs b/src/test/compile-fail/issue-28568.rs index 1dfff144cef98..7c051784f61a7 100644 --- a/src/test/compile-fail/issue-28568.rs +++ b/src/test/compile-fail/issue-28568.rs @@ -11,12 +11,12 @@ struct MyStruct; impl Drop for MyStruct { -//~^ ERROR conflicting implementations of trait +//~^ NOTE conflicting implementation is here fn drop(&mut self) { } } impl Drop for MyStruct { -//~^ NOTE conflicting implementation is here +//~^ ERROR conflicting implementations of trait fn drop(&mut self) { } } diff --git a/src/test/compile-fail/private-in-public-warn.rs b/src/test/compile-fail/private-in-public-warn.rs index 9aab06ce14ee1..b9d632a8cf07e 100644 --- a/src/test/compile-fail/private-in-public-warn.rs +++ b/src/test/compile-fail/private-in-public-warn.rs @@ -198,9 +198,11 @@ mod aliases_pub { use self::m::PubTr as PrivUseAliasTr; type PrivAlias = m::Pub2; trait PrivTr { + type AssocAlias; + } + impl PrivTr for Priv { type AssocAlias = m::Pub3; } - impl PrivTr for Priv {} pub fn f1(arg: PrivUseAlias) {} // OK @@ -245,9 +247,11 @@ mod aliases_priv { use self::PrivTr1 as PrivUseAliasTr; type PrivAlias = Priv2; trait PrivTr { + type AssocAlias; + } + impl PrivTr for Priv { type AssocAlias = Priv3; } - impl PrivTr for Priv {} pub trait Tr1: PrivUseAliasTr {} //~ WARN private trait in public interface //~^ WARNING hard error diff --git a/src/test/compile-fail/specialization/README.md b/src/test/compile-fail/specialization/README.md new file mode 100644 index 0000000000000..f2b4bf946c537 --- /dev/null +++ b/src/test/compile-fail/specialization/README.md @@ -0,0 +1,21 @@ +This directory contains the test for incorrect usage of specialization that +should lead to compile failure. Those tests break down into a few categories: + +- Feature gating + - [On use of the `default` keyword](specialization-feature-gate-default.rs) + - [On overlapping impls](specialization-feature-gate-overlap.rs) + +- Overlap checking with specialization enabled + - [Basic overlap scenarios](specialization-overlap.rs) + - Includes purely structural overlap + - Includes purely trait-based overlap + - Includes mix + - [Overlap with differing polarity](specialization-overlap-negative.rs) + +- [Attempt to specialize without using `default`](specialization-no-default.rs) + +- [Attempt to change impl polarity in a specialization](specialization-polarity.rs) + +- Attempt to rely on projection of a `default` type + - [Rely on it externally in both generic and monomorphic contexts](specialization-default-projection.rs) + - [Rely on it both within an impl and outside it](specialization-default-types.rs) diff --git a/src/test/compile-fail/specialization/specialization-default-projection.rs b/src/test/compile-fail/specialization/specialization-default-projection.rs new file mode 100644 index 0000000000000..96cbd7a485251 --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-default-projection.rs @@ -0,0 +1,46 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Make sure we can't project defaulted associated types + +trait Foo { + type Assoc; +} + +impl Foo for T { + default type Assoc = (); +} + +impl Foo for u8 { + type Assoc = String; +} + +fn generic() -> ::Assoc { + // `T` could be some downstream crate type that specializes (or, + // for that matter, `u8`). + + () //~ ERROR mismatched types +} + +fn monomorphic() -> () { + // Even though we know that `()` is not specialized in a + // downstream crate, typeck refuses to project here. + + generic::<()>() //~ ERROR mismatched types +} + +fn main() { + // No error here, we CAN project from `u8`, as there is no `default` + // in that impl. + let s: String = generic::(); + println!("{}", s); // bad news if this all compiles +} diff --git a/src/test/compile-fail/specialization/specialization-default-types.rs b/src/test/compile-fail/specialization/specialization-default-types.rs new file mode 100644 index 0000000000000..18acecb42296a --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-default-types.rs @@ -0,0 +1,45 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// It should not be possible to use the concrete value of a defaulted +// associated type in the impl defining it -- otherwise, what happens +// if it's overridden? + +#![feature(specialization)] + +trait Example { + type Output; + fn generate(self) -> Self::Output; +} + +impl Example for T { + default type Output = Box; + default fn generate(self) -> Self::Output { + Box::new(self) //~ ERROR mismatched types + } +} + +impl Example for bool { + type Output = bool; + fn generate(self) -> bool { self } +} + +fn trouble(t: T) -> Box { + Example::generate(t) //~ ERROR mismatched types +} + +fn weaponize() -> bool { + let b: Box = trouble(true); + *b +} + +fn main() { + weaponize(); +} diff --git a/src/test/compile-fail/specialization/specialization-feature-gate-default.rs b/src/test/compile-fail/specialization/specialization-feature-gate-default.rs new file mode 100644 index 0000000000000..e7c194ce84df9 --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-feature-gate-default.rs @@ -0,0 +1,21 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that specialization must be ungated to use the `default` keyword + +trait Foo { + fn foo(&self); +} + +impl Foo for T { + default fn foo(&self) {} //~ ERROR specialization is unstable +} + +fn main() {} diff --git a/src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs b/src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs new file mode 100644 index 0000000000000..d11ab56ff7e87 --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-feature-gate-overlap.rs @@ -0,0 +1,25 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that writing an overlapping impl is not allow unless specialization is ungated. + +trait Foo { + fn foo(&self); +} + +impl Foo for T { + fn foo(&self) {} +} + +impl Foo for u8 { //~ ERROR E0119 + fn foo(&self) {} +} + +fn main() {} diff --git a/src/test/compile-fail/specialization/specialization-no-default.rs b/src/test/compile-fail/specialization/specialization-no-default.rs new file mode 100644 index 0000000000000..961561685437a --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-no-default.rs @@ -0,0 +1,95 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Check a number of scenarios in which one impl tries to override another, +// without correctly using `default`. + +//////////////////////////////////////////////////////////////////////////////// +// Test 1: one layer of specialization, multiple methods, missing `default` +//////////////////////////////////////////////////////////////////////////////// + +trait Foo { + fn foo(&self); + fn bar(&self); +} + +impl Foo for T { + fn foo(&self) {} + fn bar(&self) {} +} + +impl Foo for u8 {} +impl Foo for u16 { + fn foo(&self) {} //~ ERROR E0520 +} +impl Foo for u32 { + fn bar(&self) {} //~ ERROR E0520 +} + +//////////////////////////////////////////////////////////////////////////////// +// Test 2: one layer of specialization, missing `default` on associated type +//////////////////////////////////////////////////////////////////////////////// + +trait Bar { + type T; +} + +impl Bar for T { + type T = u8; +} + +impl Bar for u8 { + type T = (); //~ ERROR E0520 +} + +//////////////////////////////////////////////////////////////////////////////// +// Test 3a: multiple layers of specialization, missing interior `default` +//////////////////////////////////////////////////////////////////////////////// + +trait Baz { + fn baz(&self); +} + +impl Baz for T { + default fn baz(&self) {} +} + +impl Baz for T { + fn baz(&self) {} +} + +impl Baz for i32 { + fn baz(&self) {} //~ ERROR E0520 +} + +//////////////////////////////////////////////////////////////////////////////// +// Test 3b: multiple layers of specialization, missing interior `default`, +// redundant `default` in bottom layer. +//////////////////////////////////////////////////////////////////////////////// + +trait Redundant { + fn redundant(&self); +} + +impl Redundant for T { + default fn redundant(&self) {} +} + +impl Redundant for T { + fn redundant(&self) {} +} + +impl Redundant for i32 { + default fn redundant(&self) {} //~ ERROR E0520 +} + +fn main() {} diff --git a/src/test/compile-fail/specialization/specialization-overlap-negative.rs b/src/test/compile-fail/specialization/specialization-overlap-negative.rs new file mode 100644 index 0000000000000..62a6d8d9b5031 --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-overlap-negative.rs @@ -0,0 +1,21 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(optin_builtin_traits)] +#![feature(specialization)] + +trait MyTrait {} + +struct TestType(::std::marker::PhantomData); + +unsafe impl Send for TestType {} +impl !Send for TestType {} //~ ERROR E0119 + +fn main() {} diff --git a/src/test/compile-fail/specialization/specialization-overlap.rs b/src/test/compile-fail/specialization/specialization-overlap.rs new file mode 100644 index 0000000000000..f579817100107 --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-overlap.rs @@ -0,0 +1,29 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +trait Foo {} +impl Foo for T {} +impl Foo for Vec {} //~ ERROR E0119 + +trait Bar {} +impl Bar for (T, u8) {} +impl Bar for (u8, T) {} //~ ERROR E0119 + +trait Baz {} +impl Baz for u8 {} +impl Baz for T {} //~ ERROR E0119 + +trait Qux {} +impl Qux for T {} +impl Qux for T {} //~ ERROR E0119 + +fn main() {} diff --git a/src/test/compile-fail/specialization/specialization-polarity.rs b/src/test/compile-fail/specialization/specialization-polarity.rs new file mode 100755 index 0000000000000..27a3e31491b82 --- /dev/null +++ b/src/test/compile-fail/specialization/specialization-polarity.rs @@ -0,0 +1,30 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure specialization cannot change impl polarity + +#![feature(optin_builtin_traits)] +#![feature(specialization)] + +trait Foo {} + +impl Foo for .. {} + +impl Foo for T {} +impl !Foo for u8 {} //~ ERROR E0119 + +trait Bar {} + +impl Bar for .. {} + +impl !Bar for T {} +impl Bar for u8 {} //~ ERROR E0119 + +fn main() {} diff --git a/src/test/parse-fail/default.rs b/src/test/parse-fail/default.rs new file mode 100644 index 0000000000000..d18401e67646c --- /dev/null +++ b/src/test/parse-fail/default.rs @@ -0,0 +1,35 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +// Test successful and unsucessful parsing of the `default` contextual keyword + +trait Foo { + fn foo() -> T; +} + +impl Foo for u8 { + default fn foo() -> T { + T::default() + } +} + +impl Foo for u16 { + pub default fn foo() -> T { + T::default() + } +} + +impl Foo for u32 { + default pub fn foo() -> T { T::default() } //~ ERROR expected one of +} + +fn main() {} diff --git a/src/test/parse-fail/issue-20711-2.rs b/src/test/parse-fail/issue-20711-2.rs index be6bd516d6fe4..a489864e3f737 100644 --- a/src/test/parse-fail/issue-20711-2.rs +++ b/src/test/parse-fail/issue-20711-2.rs @@ -16,6 +16,6 @@ impl Foo { fn foo() {} #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` +} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` fn main() {} diff --git a/src/test/parse-fail/issue-20711.rs b/src/test/parse-fail/issue-20711.rs index d1d8d3acf9187..d9789d55a6faf 100644 --- a/src/test/parse-fail/issue-20711.rs +++ b/src/test/parse-fail/issue-20711.rs @@ -14,6 +14,6 @@ struct Foo; impl Foo { #[stable(feature = "rust1", since = "1.0.0")] -} //~ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}` +} //~ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe` fn main() {} diff --git a/src/test/parse-fail/removed-syntax-static-fn.rs b/src/test/parse-fail/removed-syntax-static-fn.rs index 7b6caad86b6cc..b4c25a75c9086 100644 --- a/src/test/parse-fail/removed-syntax-static-fn.rs +++ b/src/test/parse-fail/removed-syntax-static-fn.rs @@ -15,4 +15,4 @@ struct S; impl S { static fn f() {} } -//~^^ ERROR expected one of `const`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static` +//~^^ ERROR expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}` diff --git a/src/test/run-pass/default-associated-types.rs b/src/test/run-pass/default-associated-types.rs index 3e6c72c993a0a..ed55d5c8b171e 100644 --- a/src/test/run-pass/default-associated-types.rs +++ b/src/test/run-pass/default-associated-types.rs @@ -10,23 +10,22 @@ #![feature(associated_type_defaults)] -trait Foo { - type Out = T; - fn foo(&self) -> Self::Out; +trait Foo { + type Out: Default + ToString = T; } impl Foo for () { - fn foo(&self) -> u32 { - 4u32 - } } -impl Foo for bool { - type Out = (); - fn foo(&self) {} +impl Foo for () { + type Out = bool; } fn main() { - assert_eq!(<() as Foo>::foo(&()), 4u32); - assert_eq!(>::foo(&true), ()); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "0"); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "false"); } diff --git a/src/test/run-pass/specialization/README.md b/src/test/run-pass/specialization/README.md new file mode 100644 index 0000000000000..1373a2cf81b3a --- /dev/null +++ b/src/test/run-pass/specialization/README.md @@ -0,0 +1,37 @@ +Tests that specialization is working correctly: + +- Dispatch + - [On methods](specialization-basics.rs), includes: + - Specialization via adding a trait bound + - Including both remote and local traits + - Specialization via pure structure (e.g. `(T, U)` vs `(T, T)`) + - Specialization via concrete types vs unknown types + - In top level of the trait reference + - Embedded within another type (`Vec` vs `Vec`) + - [Specialization based on super trait relationships](specialization-super-traits.rs) + - [On assoc fns](specialization-assoc-fns.rs) + - [Ensure that impl order doesn't matter](specialization-out-of-order.rs) + +- Item inheritance + - [Correct default cascading for methods](specialization-default-methods.rs) + - Inheritance works across impls with varying generics + - [With projections](specialization-translate-projections.rs) + - [With projections that involve input types](specialization-translate-projections-with-params.rs) + +- Normalization issues + - [Non-default assoc types can be projected](specialization-projection.rs) + - Including non-specialized cases + - Including specialized cases + - [Specialized Impls can happen on projections](specialization-on-projection.rs) + - [Projections and aliases play well together](specialization-projection-alias.rs) + - [Projections involving specialization allowed in the trait ref for impls, and overlap can still be determined](specialization-overlap-projection.rs) + - Only works for the simple case where the most specialized impl directly + provides a non-`default` associated type + +- Across crates + - [For traits defined in upstream crate](specialization-allowed-cross-crate.rs) + - [Full method dispatch tests, drawing from upstream crate](specialization-cross-crate.rs) + - Including *additional* local specializations + - [Full method dispatch tests, *without* turning on specialization in local crate](specialization-cross-crate-no-gate.rs) + - [Test that defaults cascade correctly from upstream crates](specialization-cross-crate-defaults.rs) + - Including *additional* local use of defaults diff --git a/src/test/run-pass/specialization/specialization-allowed-cross-crate.rs b/src/test/run-pass/specialization/specialization-allowed-cross-crate.rs new file mode 100644 index 0000000000000..6b999f3835835 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-allowed-cross-crate.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:go_trait.rs + +#![feature(specialization)] + +extern crate go_trait; + +use go_trait::{Go,GoMut}; +use std::fmt::Debug; +use std::default::Default; + +struct MyThingy; + +impl Go for MyThingy { + fn go(&self, arg: isize) { } +} + +impl GoMut for MyThingy { + fn go_mut(&mut self, arg: isize) { } +} + +fn main() { } diff --git a/src/test/run-pass/specialization/specialization-assoc-fns.rs b/src/test/run-pass/specialization/specialization-assoc-fns.rs new file mode 100644 index 0000000000000..577f217862da3 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-assoc-fns.rs @@ -0,0 +1,37 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that non-method associated functions can be specialized + +#![feature(specialization)] + +trait Foo { + fn mk() -> Self; +} + +impl Foo for T { + default fn mk() -> T { + T::default() + } +} + +impl Foo for Vec { + fn mk() -> Vec { + vec![0] + } +} + +fn main() { + let v1: Vec = Foo::mk(); + let v2: Vec = Foo::mk(); + + assert!(v1.len() == 0); + assert!(v2.len() == 1); +} diff --git a/src/test/run-pass/specialization/specialization-basics.rs b/src/test/run-pass/specialization/specialization-basics.rs new file mode 100644 index 0000000000000..b11495e9edf1a --- /dev/null +++ b/src/test/run-pass/specialization/specialization-basics.rs @@ -0,0 +1,106 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Tests a variety of basic specialization scenarios and method +// dispatch for them. + +trait Foo { + fn foo(&self) -> &'static str; +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic" + } +} + +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone" + } +} + +impl Foo for (T, U) where T: Clone, U: Clone { + default fn foo(&self) -> &'static str { + "generic pair" + } +} + +impl Foo for (T, T) { + default fn foo(&self) -> &'static str { + "generic uniform pair" + } +} + +impl Foo for (u8, u32) { + default fn foo(&self) -> &'static str { + "(u8, u32)" + } +} + +impl Foo for (u8, u8) { + default fn foo(&self) -> &'static str { + "(u8, u8)" + } +} + +impl Foo for Vec { + default fn foo(&self) -> &'static str { + "generic Vec" + } +} + +impl Foo for Vec { + fn foo(&self) -> &'static str { + "Vec" + } +} + +impl Foo for String { + fn foo(&self) -> &'static str { + "String" + } +} + +impl Foo for i32 { + fn foo(&self) -> &'static str { + "i32" + } +} + +struct NotClone; + +trait MyMarker {} +impl Foo for T { + default fn foo(&self) -> &'static str { + "generic Clone + MyMarker" + } +} + +#[derive(Clone)] +struct MarkedAndClone; +impl MyMarker for MarkedAndClone {} + +fn main() { + assert!(NotClone.foo() == "generic"); + assert!(0u8.foo() == "generic Clone"); + assert!(vec![NotClone].foo() == "generic"); + assert!(vec![0u8].foo() == "generic Vec"); + assert!(vec![0i32].foo() == "Vec"); + assert!(0i32.foo() == "i32"); + assert!(String::new().foo() == "String"); + assert!(((), 0).foo() == "generic pair"); + assert!(((), ()).foo() == "generic uniform pair"); + assert!((0u8, 0u32).foo() == "(u8, u32)"); + assert!((0u8, 0u8).foo() == "(u8, u8)"); + assert!(MarkedAndClone.foo() == "generic Clone + MyMarker"); +} diff --git a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs new file mode 100644 index 0000000000000..bc695ea821d0a --- /dev/null +++ b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs @@ -0,0 +1,49 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:specialization_cross_crate_defaults.rs + +#![feature(specialization)] + +extern crate specialization_cross_crate_defaults; + +use specialization_cross_crate_defaults::*; + +struct LocalDefault; +struct LocalOverride; + +impl Foo for LocalDefault {} + +impl Foo for LocalOverride { + fn foo(&self) -> bool { true } +} + +fn test_foo() { + assert!(0i8.foo() == false); + assert!(0i32.foo() == false); + assert!(0i64.foo() == true); + + assert!(LocalDefault.foo() == false); + assert!(LocalOverride.foo() == true); +} + +fn test_bar() { + assert!(0u8.bar() == 0); + assert!(0i32.bar() == 1); + assert!("hello".bar() == 0); + assert!(vec![()].bar() == 2); + assert!(vec![0i32].bar() == 2); + assert!(vec![0i64].bar() == 3); +} + +fn main() { + test_foo(); + test_bar(); +} diff --git a/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs b/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs new file mode 100644 index 0000000000000..b9548539e1649 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-cross-crate-no-gate.rs @@ -0,0 +1,29 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that specialization works even if only the upstream crate enables it + +// aux-build:specialization_cross_crate.rs + +extern crate specialization_cross_crate; + +use specialization_cross_crate::*; + +fn main() { + assert!(0u8.foo() == "generic Clone"); + assert!(vec![0u8].foo() == "generic Vec"); + assert!(vec![0i32].foo() == "Vec"); + assert!(0i32.foo() == "i32"); + assert!(String::new().foo() == "String"); + assert!(((), 0).foo() == "generic pair"); + assert!(((), ()).foo() == "generic uniform pair"); + assert!((0u8, 0u32).foo() == "(u8, u32)"); + assert!((0u8, 0u8).foo() == "(u8, u8)"); +} diff --git a/src/test/run-pass/specialization/specialization-cross-crate.rs b/src/test/run-pass/specialization/specialization-cross-crate.rs new file mode 100644 index 0000000000000..7593ac4fb1dd2 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-cross-crate.rs @@ -0,0 +1,58 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:specialization_cross_crate.rs + +#![feature(specialization)] + +extern crate specialization_cross_crate; + +use specialization_cross_crate::*; + +struct NotClone; + +#[derive(Clone)] +struct MarkedAndClone; +impl MyMarker for MarkedAndClone {} + +struct MyType(T); +impl Foo for MyType { + default fn foo(&self) -> &'static str { + "generic MyType" + } +} + +impl Foo for MyType { + fn foo(&self) -> &'static str { + "MyType" + } +} + +struct MyOtherType; +impl Foo for MyOtherType {} + +fn main() { + assert!(NotClone.foo() == "generic"); + assert!(0u8.foo() == "generic Clone"); + assert!(vec![NotClone].foo() == "generic"); + assert!(vec![0u8].foo() == "generic Vec"); + assert!(vec![0i32].foo() == "Vec"); + assert!(0i32.foo() == "i32"); + assert!(String::new().foo() == "String"); + assert!(((), 0).foo() == "generic pair"); + assert!(((), ()).foo() == "generic uniform pair"); + assert!((0u8, 0u32).foo() == "(u8, u32)"); + assert!((0u8, 0u8).foo() == "(u8, u8)"); + assert!(MarkedAndClone.foo() == "generic Clone + MyMarker"); + + assert!(MyType(()).foo() == "generic MyType"); + assert!(MyType(0u8).foo() == "MyType"); + assert!(MyOtherType.foo() == "generic"); +} diff --git a/src/test/run-pass/specialization/specialization-default-methods.rs b/src/test/run-pass/specialization/specialization-default-methods.rs new file mode 100644 index 0000000000000..3f0f21ff03f27 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-default-methods.rs @@ -0,0 +1,94 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Test that default methods are cascaded correctly + +// First, test only use of explicit `default` items: + +trait Foo { + fn foo(&self) -> bool; +} + +// Specialization tree for Foo: +// +// T +// / \ +// i32 i64 + +impl Foo for T { + default fn foo(&self) -> bool { false } +} + +impl Foo for i32 {} + +impl Foo for i64 { + fn foo(&self) -> bool { true } +} + +fn test_foo() { + assert!(0i8.foo() == false); + assert!(0i32.foo() == false); + assert!(0i64.foo() == true); +} + +// Next, test mixture of explicit `default` and provided methods: + +trait Bar { + fn bar(&self) -> i32 { 0 } +} + +// Specialization tree for Bar. +// Uses of $ designate that method is provided +// +// $Bar (the trait) +// | +// T +// /|\ +// / | \ +// / | \ +// / | \ +// / | \ +// / | \ +// $i32 &str $Vec +// /\ +// / \ +// Vec $Vec + +// use the provided method +impl Bar for T {} + +impl Bar for i32 { + fn bar(&self) -> i32 { 1 } +} +impl<'a> Bar for &'a str {} + +impl Bar for Vec { + default fn bar(&self) -> i32 { 2 } +} +impl Bar for Vec {} +impl Bar for Vec { + fn bar(&self) -> i32 { 3 } +} + +fn test_bar() { + assert!(0u8.bar() == 0); + assert!(0i32.bar() == 1); + assert!("hello".bar() == 0); + assert!(vec![()].bar() == 2); + assert!(vec![0i32].bar() == 2); + assert!(vec![0i64].bar() == 3); +} + +fn main() { + test_foo(); + test_bar(); +} diff --git a/src/test/run-pass/specialization/specialization-on-projection.rs b/src/test/run-pass/specialization/specialization-on-projection.rs new file mode 100644 index 0000000000000..acf78def1b967 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-on-projection.rs @@ -0,0 +1,31 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Ensure that specialization works for impls defined directly on a projection + +trait Foo {} + +trait Assoc { + type Item; +} + +impl Foo for T {} + +struct Struct; + +impl Assoc for Struct { + type Item = u8; +} + +impl Foo for Struct {} + +fn main() {} diff --git a/src/test/run-pass/specialization/specialization-out-of-order.rs b/src/test/run-pass/specialization/specialization-out-of-order.rs new file mode 100644 index 0000000000000..2d293f494a347 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-out-of-order.rs @@ -0,0 +1,27 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that you can list the more specific impl before the more general one. + +#![feature(specialization)] + +trait Foo { + type Out; +} + +impl Foo for bool { + type Out = (); +} + +impl Foo for T { + default type Out = bool; +} + +fn main() {} diff --git a/src/test/run-pass/specialization/specialization-overlap-projection.rs b/src/test/run-pass/specialization/specialization-overlap-projection.rs new file mode 100644 index 0000000000000..20046ee66b0e2 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-overlap-projection.rs @@ -0,0 +1,33 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that impls on projected self types can resolve overlap, even when the +// projections involve specialization, so long as the associated type is +// provided by the most specialized impl. + +#![feature(specialization)] + +trait Assoc { + type Output; +} + +impl Assoc for T { + default type Output = bool; +} + +impl Assoc for u8 { type Output = u8; } +impl Assoc for u16 { type Output = u16; } + +trait Foo {} +impl Foo for u32 {} +impl Foo for ::Output {} +impl Foo for ::Output {} + +fn main() {} diff --git a/src/test/run-pass/specialization/specialization-projection-alias.rs b/src/test/run-pass/specialization/specialization-projection-alias.rs new file mode 100644 index 0000000000000..7fce1cca582c1 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-projection-alias.rs @@ -0,0 +1,32 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Regression test for ICE when combining specialized associated types and type +// aliases + +trait Id_ { + type Out; +} + +type Id = ::Out; + +impl Id_ for T { + default type Out = T; +} + +fn test_proection() { + let x: Id = panic!(); +} + +fn main() { + +} diff --git a/src/test/run-pass/specialization/specialization-projection.rs b/src/test/run-pass/specialization/specialization-projection.rs new file mode 100644 index 0000000000000..4e0bdec297fe2 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-projection.rs @@ -0,0 +1,49 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Make sure we *can* project non-defaulted associated types +// cf compile-fail/specialization-default-projection.rs + +// First, do so without any use of specialization + +trait Foo { + type Assoc; +} + +impl Foo for T { + type Assoc = (); +} + +fn generic_foo() -> ::Assoc { + () +} + +// Next, allow for one layer of specialization + +trait Bar { + type Assoc; +} + +impl Bar for T { + default type Assoc = (); +} + +impl Bar for T { + type Assoc = u8; +} + +fn generic_bar_clone() -> ::Assoc { + 0u8 +} + +fn main() { +} diff --git a/src/test/run-pass/specialization/specialization-super-traits.rs b/src/test/run-pass/specialization/specialization-super-traits.rs new file mode 100644 index 0000000000000..a9b3bfca53d18 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-super-traits.rs @@ -0,0 +1,25 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(specialization)] + +// Test that you can specialize via an explicit trait hierarchy + +// FIXME: this doesn't work yet... + +trait Parent {} +trait Child: Parent {} + +trait Foo {} + +impl Foo for T {} +impl Foo for T {} + +fn main() {} diff --git a/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs new file mode 100644 index 0000000000000..647d5523c376c --- /dev/null +++ b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs @@ -0,0 +1,40 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Ensure that provided items are inherited properly even when impls vary in +// type parameters *and* rely on projections, and the type parameters are input +// types on the trait. + +#![feature(specialization)] + +trait Trait { + fn convert(&self) -> T; +} +trait WithAssoc { + type Item; + fn as_item(&self) -> &Self::Item; +} + +impl Trait for T where T: WithAssoc, U: Clone { + fn convert(&self) -> U { + self.as_item().clone() + } +} + +impl WithAssoc for u8 { + type Item = u8; + fn as_item(&self) -> &u8 { self } +} + +impl Trait for u8 {} + +fn main() { + assert!(3u8.convert() == 3u8); +} diff --git a/src/test/run-pass/specialization/specialization-translate-projections.rs b/src/test/run-pass/specialization/specialization-translate-projections.rs new file mode 100644 index 0000000000000..11e1d997fdda0 --- /dev/null +++ b/src/test/run-pass/specialization/specialization-translate-projections.rs @@ -0,0 +1,41 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Ensure that provided items are inherited properly even when impls vary in +// type parameters *and* rely on projections. + +#![feature(specialization)] + +use std::convert::Into; + +trait Trait { + fn to_u8(&self) -> u8; +} +trait WithAssoc { + type Item; + fn to_item(&self) -> Self::Item; +} + +impl Trait for T where T: WithAssoc, U: Into { + fn to_u8(&self) -> u8 { + self.to_item().into() + } +} + +impl WithAssoc for u8 { + type Item = u8; + fn to_item(&self) -> u8 { *self } +} + +impl Trait for u8 {} + +fn main() { + assert!(3u8.to_u8() == 3u8); +} diff --git a/src/test/run-pass/xcrate-associated-type-defaults.rs b/src/test/run-pass/xcrate-associated-type-defaults.rs index 1b6de3b2f7bcf..2dacbe0966ee3 100644 --- a/src/test/run-pass/xcrate-associated-type-defaults.rs +++ b/src/test/run-pass/xcrate-associated-type-defaults.rs @@ -13,6 +13,26 @@ extern crate xcrate_associated_type_defaults; use xcrate_associated_type_defaults::Foo; +struct LocalDefault; +impl Foo for LocalDefault {} + +struct LocalOverride; +impl Foo for LocalOverride { + type Out = bool; +} + fn main() { - ().bar(5); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "0"); + assert_eq!( + <() as Foo>::Out::default().to_string(), + "false"); + + assert_eq!( + >::Out::default().to_string(), + "0"); + assert_eq!( + >::Out::default().to_string(), + "false"); }