From 39ee66ab82fc38a13d046ac1caa1eb55edfa8901 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Mon, 9 Mar 2020 21:59:13 +0000 Subject: [PATCH] Consider well-formed predicates in min-specialization --- .../impl_wf_check/min_specialization.rs | 27 ++++++++++++++--- .../implcit-well-formed-bounds.rs | 30 +++++++++++++++++++ .../specialization_super_trait.rs | 18 +++++++++++ .../specialization_super_trait.stderr | 11 +++++++ 4 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs create mode 100644 src/test/ui/specialization/min_specialization/specialization_super_trait.rs create mode 100644 src/test/ui/specialization/min_specialization/specialization_super_trait.stderr diff --git a/src/librustc_typeck/impl_wf_check/min_specialization.rs b/src/librustc_typeck/impl_wf_check/min_specialization.rs index cd52dae5f9c..e96a8c454b8 100644 --- a/src/librustc_typeck/impl_wf_check/min_specialization.rs +++ b/src/librustc_typeck/impl_wf_check/min_specialization.rs @@ -21,8 +21,9 @@ //! in the *unconstrained* substs for `impl2`. A parameter is constrained if //! its value is completely determined by an associated type projection //! predicate. -//! 4. Check that all predicates on `impl1` also exist on `impl2` (after -//! matching substs). +//! 4. Check that all predicates on `impl1` either exist on `impl2` (after +//! matching substs), or are well-formed predicates for the trait's type +//! arguments. //! //! ## Example //! @@ -129,7 +130,7 @@ fn check_always_applicable( check_static_lifetimes(tcx, &parent_substs, span); check_duplicate_params(tcx, impl1_substs, &parent_substs, span); - check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); + check_predicates(infcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); } } @@ -282,14 +283,17 @@ fn check_static_lifetimes<'tcx>( /// * on the base `impl impl2` /// * Currently this check is done using syntactic equality, which is /// conservative but generally sufficient. +/// * a well-formed predicate of a type argument of the trait being implemented, +/// including the `Self`-type. fn check_predicates<'tcx>( - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'_, 'tcx>, impl1_def_id: DefId, impl1_substs: SubstsRef<'tcx>, impl2_node: Node, impl2_substs: SubstsRef<'tcx>, span: Span, ) { + let tcx = infcx.tcx; let impl1_predicates = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs); let mut impl2_predicates = if impl2_node.is_from_trait() { // Always applicable traits have to be always applicable without any @@ -329,6 +333,21 @@ fn check_predicates<'tcx>( }) .copied() .collect(); + + // Include the well-formed predicates of the type parameters of the impl. + for ty in tcx.impl_trait_ref(impl1_def_id).unwrap().substs.types() { + if let Some(obligations) = wf::obligations( + infcx, + tcx.param_env(impl1_def_id), + tcx.hir().as_local_hir_id(impl1_def_id).unwrap(), + ty, + span, + ) { + impl2_predicates + .predicates + .extend(obligations.into_iter().map(|obligation| obligation.predicate)) + } + } impl2_predicates.predicates.extend(traits::elaborate_predicates(tcx, always_applicable_traits)); for predicate in impl1_predicates.predicates { diff --git a/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs b/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs new file mode 100644 index 00000000000..98d7f919435 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs @@ -0,0 +1,30 @@ +// Test that specializing on the well-formed predicates of the trait and +// self-type of an impl is allowed. + +// check-pass + +#![feature(min_specialization)] + +struct OrdOnly(T); + +trait SpecTrait { + fn f(); +} + +impl SpecTrait for T { + default fn f() {} +} + +impl SpecTrait<()> for OrdOnly { + fn f() {} +} + +impl SpecTrait> for () { + fn f() {} +} + +impl SpecTrait<(OrdOnly, OrdOnly)> for &[OrdOnly] { + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.rs b/src/test/ui/specialization/min_specialization/specialization_super_trait.rs new file mode 100644 index 00000000000..145f376edf9 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.rs @@ -0,0 +1,18 @@ +// Test that supertraits can't be assumed in impls of +// `rustc_specialization_trait`, as such impls would +// allow specializing on the supertrait. + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +trait SpecMarker: Default { + fn f(); +} + +impl SpecMarker for T { + //~^ ERROR cannot specialize + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr new file mode 100644 index 00000000000..154c839c6da --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on trait `std::default::Default` + --> $DIR/specialization_super_trait.rs:13:1 + | +LL | / impl SpecMarker for T { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error +