From bb0c8ef373317843c1df7e9110e4bd0978ff47eb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 8 Jan 2015 06:58:41 -0500 Subject: [PATCH] Normalize types in impls, add test for coherence failure. Fixes #20624. --- src/librustc/lib.rs | 1 - src/librustc/middle/subst.rs | 11 ++++ src/librustc/middle/traits/coherence.rs | 17 ++++-- src/librustc/middle/traits/project.rs | 7 +++ src/librustc/middle/traits/select.rs | 58 ++++++++++-------- .../associated-types-coherence-failure.rs | 59 +++++++++++++++++++ 6 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 src/test/compile-fail/associated-types-coherence-failure.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index e0143917a7c..db67bb4ba7f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -28,7 +28,6 @@ #![feature(slicing_syntax, unsafe_destructor)] #![feature(box_syntax)] #![feature(rustc_diagnostic_macros)] -#![feature(old_impl_check)] extern crate arena; extern crate flate; diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 5fbae6c359d..9ac6b8a86b6 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -313,6 +313,17 @@ impl VecPerParamSpace { self.content.insert(limit, value); } + /// Appends `values` to the vector associated with `space`. + /// + /// Unlike the `extend` method in `Vec`, this should not be assumed + /// to be a cheap operation (even when amortized over many calls). + pub fn extend>(&mut self, space: ParamSpace, mut values: I) { + // This could be made more efficient, obviously. + for item in values { + self.push(space, item); + } + } + pub fn pop(&mut self, space: ParamSpace) -> Option { let (start, limit) = self.limits(space); if start == limit { diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index 489731e7554..b6d45ab89e7 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -12,6 +12,7 @@ use super::SelectionContext; use super::{Obligation, ObligationCause}; +use super::project; use super::util; use middle::subst::{Subst}; @@ -34,22 +35,28 @@ pub fn impl_can_satisfy(infcx: &InferCtxt, impl1_def_id.repr(infcx.tcx), impl2_def_id.repr(infcx.tcx)); + let param_env = ty::empty_parameter_environment(infcx.tcx); + let mut selcx = SelectionContext::intercrate(infcx, ¶m_env); + let cause = ObligationCause::dummy(); + // `impl1` provides an implementation of `Foo for Z`. let impl1_substs = util::fresh_substs_for_impl(infcx, DUMMY_SP, impl1_def_id); let impl1_trait_ref = (*ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap()).subst(infcx.tcx, &impl1_substs); + let impl1_trait_ref = + project::normalize(&mut selcx, cause.clone(), &impl1_trait_ref); // Determine whether `impl2` can provide an implementation for those // same types. - let param_env = ty::empty_parameter_environment(infcx.tcx); - let mut selcx = SelectionContext::intercrate(infcx, ¶m_env); - let obligation = Obligation::new(ObligationCause::dummy(), + let obligation = Obligation::new(cause, ty::Binder(ty::TraitPredicate { - trait_ref: Rc::new(impl1_trait_ref), + trait_ref: Rc::new(impl1_trait_ref.value), })); debug!("impl_can_satisfy(obligation={})", obligation.repr(infcx.tcx)); - selcx.evaluate_impl(impl2_def_id, &obligation) + selcx.evaluate_impl(impl2_def_id, &obligation) && + impl1_trait_ref.obligations.iter().all( + |o| selcx.evaluate_obligation(o)) } #[allow(missing_copy_implementations)] diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index aaf5df4ce4a..1a7e712f180 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -243,6 +243,13 @@ pub struct Normalized<'tcx,T> { pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>; +impl<'tcx,T> Normalized<'tcx,T> { + fn with(self, value: U) -> Normalized<'tcx,U> { + Normalized { value: value, obligations: self.obligations } + } +} + + pub fn normalize_projection_type<'a,'b,'tcx>( selcx: &'a mut SelectionContext<'b,'tcx>, projection_ty: ty::ProjectionTy<'tcx>, diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index f42f43d2576..5a491d71cf4 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -19,6 +19,7 @@ use self::EvaluationResult::*; use super::{DerivedObligationCause}; use super::{project}; +use super::project::Normalized; use super::{PredicateObligation, Obligation, TraitObligation, ObligationCause}; use super::{ObligationCauseCode, BuiltinDerivedObligation}; use super::{SelectionError, Unimplemented, Overflow, OutputTypeParameterMismatch}; @@ -1160,7 +1161,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let impl_trait_ref = ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap(); let impl_trait_ref = - impl_trait_ref.subst(self.tcx(), &impl_substs); + impl_trait_ref.subst(self.tcx(), &impl_substs.value); let poly_impl_trait_ref = ty::Binder(impl_trait_ref); let origin = @@ -1731,7 +1732,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let substs = self.rematch_impl(impl_def_id, obligation, snapshot, &skol_map, skol_obligation_trait_ref.trait_ref); - debug!("confirm_impl_candidate substs={:?}", substs); + debug!("confirm_impl_candidate substs={}", substs.repr(self.tcx())); Ok(self.vtable_impl(impl_def_id, substs, obligation.cause.clone(), obligation.recursion_depth + 1, skol_map, snapshot)) }) @@ -1739,7 +1740,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn vtable_impl(&mut self, impl_def_id: ast::DefId, - substs: Substs<'tcx>, + substs: Normalized<'tcx, Substs<'tcx>>, cause: ObligationCause<'tcx>, recursion_depth: uint, skol_map: infer::SkolemizationMap, @@ -1752,21 +1753,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { recursion_depth, skol_map.repr(self.tcx())); - let impl_predicates = - self.impl_predicates(cause, + let mut impl_obligations = + self.impl_obligations(cause, recursion_depth, impl_def_id, - &substs, + &substs.value, skol_map, snapshot); - debug!("vtable_impl: impl_def_id={} impl_predicates={}", + debug!("vtable_impl: impl_def_id={} impl_obligations={}", impl_def_id.repr(self.tcx()), - impl_predicates.repr(self.tcx())); + impl_obligations.repr(self.tcx())); + + impl_obligations.extend(TypeSpace, substs.obligations.into_iter()); VtableImplData { impl_def_id: impl_def_id, - substs: substs, - nested: impl_predicates } + substs: substs.value, + nested: impl_obligations } } fn confirm_object_candidate(&mut self, @@ -1950,7 +1953,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { snapshot: &infer::CombinedSnapshot, skol_map: &infer::SkolemizationMap, skol_obligation_trait_ref: Rc>) - -> Substs<'tcx> + -> Normalized<'tcx, Substs<'tcx>> { match self.match_impl(impl_def_id, obligation, snapshot, skol_map, skol_obligation_trait_ref) { @@ -1972,7 +1975,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { snapshot: &infer::CombinedSnapshot, skol_map: &infer::SkolemizationMap, skol_obligation_trait_ref: Rc>) - -> Result, ()> + -> Result>, ()> { let impl_trait_ref = ty::impl_trait_ref(self.tcx(), impl_def_id).unwrap(); @@ -1990,6 +1993,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let impl_trait_ref = impl_trait_ref.subst(self.tcx(), &impl_substs); + let impl_trait_ref = + project::normalize_with_depth(self, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &impl_trait_ref); + debug!("match_impl(impl_def_id={}, obligation={}, \ impl_trait_ref={}, skol_obligation_trait_ref={})", impl_def_id.repr(self.tcx()), @@ -2000,7 +2009,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let origin = infer::RelateOutputImplTypes(obligation.cause.span); match self.infcx.sub_trait_refs(false, origin, - impl_trait_ref, + impl_trait_ref.value.clone(), skol_obligation_trait_ref) { Ok(()) => { } Err(e) => { @@ -2020,7 +2029,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } debug!("match_impl: success impl_substs={}", impl_substs.repr(self.tcx())); - Ok(impl_substs) + Ok(Normalized { value: impl_substs, + obligations: impl_trait_ref.obligations }) } fn fast_reject_trait_refs(&mut self, @@ -2161,14 +2171,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn impl_predicates(&mut self, - cause: ObligationCause<'tcx>, - recursion_depth: uint, - impl_def_id: ast::DefId, - impl_substs: &Substs<'tcx>, - skol_map: infer::SkolemizationMap, - snapshot: &infer::CombinedSnapshot) - -> VecPerParamSpace> + fn impl_obligations(&mut self, + cause: ObligationCause<'tcx>, + recursion_depth: uint, + impl_def_id: ast::DefId, + impl_substs: &Substs<'tcx>, + skol_map: infer::SkolemizationMap, + snapshot: &infer::CombinedSnapshot) + -> VecPerParamSpace> { let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics; let bounds = impl_generics.to_bounds(self.tcx(), impl_substs); @@ -2181,9 +2191,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause, recursion_depth, &normalized_bounds.value); - for obligation in normalized_bounds.obligations.into_iter() { - impl_obligations.push(TypeSpace, obligation); - } + impl_obligations.extend(TypeSpace, normalized_bounds.obligations.into_iter()); impl_obligations } diff --git a/src/test/compile-fail/associated-types-coherence-failure.rs b/src/test/compile-fail/associated-types-coherence-failure.rs new file mode 100644 index 00000000000..95a68dd6698 --- /dev/null +++ b/src/test/compile-fail/associated-types-coherence-failure.rs @@ -0,0 +1,59 @@ +// 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. + +// Test that coherence detects overlap when some of the types in the +// impls are projections of associated type. Issue #20624. + +use std::ops::Deref; + +pub struct Cow<'a, B: ?Sized>; + +/// Trait for moving into a `Cow` +pub trait IntoCow<'a, B: ?Sized> { + /// Moves `self` into `Cow` + fn into_cow(self) -> Cow<'a, B>; +} + +impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned { +//~^ ERROR E0119 + fn into_cow(self) -> Cow<'a, B> { + self + } +} + +impl<'a, B: ?Sized> IntoCow<'a, B> for ::Owned where B: ToOwned { +//~^ ERROR E0119 + fn into_cow(self) -> Cow<'a, B> { + Cow + } +} + +impl<'a, B: ?Sized> IntoCow<'a, B> for &'a B where B: ToOwned { + fn into_cow(self) -> Cow<'a, B> { + Cow + } +} + +impl ToOwned for u8 { + type Owned = &'static u8; + fn to_owned(&self) -> &'static u8 { panic!() } +} + +/// A generalization of Clone to borrowed data. +pub trait ToOwned { + type Owned; + + /// Create owned data from borrowed data, usually by copying. + fn to_owned(&self) -> Self::Owned; +} + + +fn main() {} +