diff --git a/src/librustc/middle/traits/specialize/mod.rs b/src/librustc/middle/traits/specialize/mod.rs index a5b3c667fc06d..624ebc545fe7c 100644 --- a/src/librustc/middle/traits/specialize/mod.rs +++ b/src/librustc/middle/traits/specialize/mod.rs @@ -25,7 +25,7 @@ use middle::def_id::DefId; use middle::infer::{self, InferCtxt, TypeOrigin}; use middle::region; use middle::subst::{Subst, Substs}; -use middle::traits::ProjectionMode; +use middle::traits::{self, ProjectionMode, ObligationCause, Normalized}; use middle::ty::{self, TyCtxt}; use syntax::codemap::DUMMY_SP; @@ -149,14 +149,21 @@ pub fn specializes(tcx: &TyCtxt, impl1_def_id: DefId, impl2_def_id: DefId) -> bo // 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 penv = tcx.construct_parameter_environment(DUMMY_SP, - &scheme.generics, - &predicates, - region::DUMMY_CODE_EXTENT); + 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, which means we take the predicates of impl1 as assumptions: infcx.parameter_environment = penv; 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/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 index 377838f2a080c..96cbd7a485251 100644 --- a/src/test/compile-fail/specialization/specialization-default-projection.rs +++ b/src/test/compile-fail/specialization/specialization-default-projection.rs @@ -28,14 +28,14 @@ fn generic() -> ::Assoc { // `T` could be some downstream crate type that specializes (or, // for that matter, `u8`). - () //~ ERROR E0308 + () //~ ERROR mismatched types } fn monomorphic() -> () { // Even though we know that `()` is not specialized in a // downstream crate, typeck refuses to project here. - generic::<()>() //~ ERROR E0308 + generic::<()>() //~ ERROR mismatched types } fn main() { diff --git a/src/test/compile-fail/specialization/specialization-default-types.rs b/src/test/compile-fail/specialization/specialization-default-types.rs index 3c2e3d5a36c37..18acecb42296a 100644 --- a/src/test/compile-fail/specialization/specialization-default-types.rs +++ b/src/test/compile-fail/specialization/specialization-default-types.rs @@ -22,7 +22,7 @@ trait Example { impl Example for T { default type Output = Box; default fn generate(self) -> Self::Output { - Box::new(self) //~ ERROR E0308 + Box::new(self) //~ ERROR mismatched types } } @@ -32,7 +32,7 @@ impl Example for bool { } fn trouble(t: T) -> Box { - Example::generate(t) //~ ERROR E0308 + Example::generate(t) //~ ERROR mismatched types } fn weaponize() -> bool { diff --git a/src/test/compile-fail/specialization/specialization-overlap.rs b/src/test/compile-fail/specialization/specialization-overlap.rs index 57529d2ae4266..f579817100107 100644 --- a/src/test/compile-fail/specialization/specialization-overlap.rs +++ b/src/test/compile-fail/specialization/specialization-overlap.rs @@ -22,4 +22,8 @@ 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/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-assoc-fns.rs b/src/test/run-pass/specialization/specialization-assoc-fns.rs index 683e0d55dc944..577f217862da3 100644 --- a/src/test/run-pass/specialization/specialization-assoc-fns.rs +++ b/src/test/run-pass/specialization/specialization-assoc-fns.rs @@ -8,6 +8,8 @@ // 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 { diff --git a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs index 750c3cf8b3ecb..bc695ea821d0a 100644 --- a/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs +++ b/src/test/run-pass/specialization/specialization-cross-crate-defaults.rs @@ -16,10 +16,22 @@ 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() { 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-default-methods.rs b/src/test/run-pass/specialization/specialization-default-methods.rs index d662c5bfa28d6..b2fad9d171f9d 100644 --- a/src/test/run-pass/specialization/specialization-default-methods.rs +++ b/src/test/run-pass/specialization/specialization-default-methods.rs @@ -10,6 +10,8 @@ #![feature(specialization)] +// Test that default methods are cascaded correctly + // First, test only use of explicit `default` items: trait Foo { diff --git a/src/test/run-pass/specialization/specialization-on-projection.rs b/src/test/run-pass/specialization/specialization-on-projection.rs index 65cbb31d22147..acf78def1b967 100644 --- a/src/test/run-pass/specialization/specialization-on-projection.rs +++ b/src/test/run-pass/specialization/specialization-on-projection.rs @@ -10,6 +10,8 @@ #![feature(specialization)] +// Ensure that specialization works for impls defined directly on a projection + trait Foo {} trait Assoc { 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 index 2250d77e08e8e..7fce1cca582c1 100644 --- a/src/test/run-pass/specialization/specialization-projection-alias.rs +++ b/src/test/run-pass/specialization/specialization-projection-alias.rs @@ -23,6 +23,10 @@ impl Id_ for T { default type Out = T; } -fn main() { +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 index d26d59896a574..4e0bdec297fe2 100644 --- a/src/test/run-pass/specialization/specialization-projection.rs +++ b/src/test/run-pass/specialization/specialization-projection.rs @@ -13,6 +13,8 @@ // 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; } @@ -21,9 +23,27 @@ impl Foo for T { type Assoc = (); } -fn generic() -> ::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-translate-projections-with-params.rs b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs index 3d90bc56f42bd..647d5523c376c 100644 --- a/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs +++ b/src/test/run-pass/specialization/specialization-translate-projections-with-params.rs @@ -8,6 +8,10 @@ // 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 { diff --git a/src/test/run-pass/specialization/specialization-translate-projections.rs b/src/test/run-pass/specialization/specialization-translate-projections.rs index d224efe8c3183..11e1d997fdda0 100644 --- a/src/test/run-pass/specialization/specialization-translate-projections.rs +++ b/src/test/run-pass/specialization/specialization-translate-projections.rs @@ -8,6 +8,9 @@ // 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; 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"); }