From c8f86cad2dda3181bfedc165d3dd4bf452770228 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 30 Sep 2021 21:42:09 +0100 Subject: [PATCH] Elaborate predicates in min_specialization checks Supertraits of specialization markers could circumvent checks for min_specialization. Elaborating predicates prevents this. --- .../rustc_trait_selection/src/traits/mod.rs | 4 +- .../src/impl_wf_check/min_specialization.rs | 48 ++++++++++++------- .../spec-marker-supertraits.rs | 29 +++++++++++ .../spec-marker-supertraits.stderr | 13 +++++ 4 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs create mode 100644 src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index b3c9cf4c173e..d777f586e23c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -64,7 +64,9 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_match::NonStructuralMatchTy; -pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; +pub use self::util::{ + elaborate_obligations, elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs, +}; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{ get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index 8ecd6034ad69..f4bb5761c19b 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -74,7 +74,7 @@ use rustc_infer::infer::{InferCtxt, RegionckMode, TyCtxtInferExt}; use rustc_infer::traits::specialization_graph::Node; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; use rustc_middle::ty::trait_def::TraitSpecializationKind; -use rustc_middle::ty::{self, InstantiatedPredicates, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::Span; use rustc_trait_selection::traits::{self, translate_substs, wf}; @@ -294,13 +294,27 @@ fn check_predicates<'tcx>( span: Span, ) { let tcx = infcx.tcx; - let impl1_predicates = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs); + let impl1_predicates: Vec<_> = traits::elaborate_predicates( + tcx, + tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs).predicates.into_iter(), + ) + .map(|obligation| obligation.predicate) + .collect(); + let mut impl2_predicates = if impl2_node.is_from_trait() { // Always applicable traits have to be always applicable without any // assumptions. - InstantiatedPredicates::empty() + Vec::new() } else { - tcx.predicates_of(impl2_node.def_id()).instantiate(tcx, impl2_substs) + traits::elaborate_predicates( + tcx, + tcx.predicates_of(impl2_node.def_id()) + .instantiate(tcx, impl2_substs) + .predicates + .into_iter(), + ) + .map(|obligation| obligation.predicate) + .collect() }; debug!( "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", @@ -322,13 +336,12 @@ fn check_predicates<'tcx>( // which is sound because we forbid impls like the following // // impl AlwaysApplicable for D { } - let always_applicable_traits = - impl1_predicates.predicates.iter().copied().filter(|&predicate| { - matches!( - trait_predicate_kind(tcx, predicate), - Some(TraitSpecializationKind::AlwaysApplicable) - ) - }); + let always_applicable_traits = impl1_predicates.iter().copied().filter(|&predicate| { + matches!( + trait_predicate_kind(tcx, predicate), + Some(TraitSpecializationKind::AlwaysApplicable) + ) + }); // Include the well-formed predicates of the type parameters of the impl. for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs { @@ -340,18 +353,19 @@ fn check_predicates<'tcx>( arg, span, ) { - impl2_predicates - .predicates - .extend(obligations.into_iter().map(|obligation| obligation.predicate)) + impl2_predicates.extend( + traits::elaborate_obligations(tcx, obligations) + .map(|obligation| obligation.predicate), + ) } } - impl2_predicates.predicates.extend( + impl2_predicates.extend( traits::elaborate_predicates(tcx, always_applicable_traits) .map(|obligation| obligation.predicate), ); - for predicate in impl1_predicates.predicates { - if !impl2_predicates.predicates.contains(&predicate) { + for predicate in impl1_predicates { + if !impl2_predicates.contains(&predicate) { check_specialization_on(tcx, predicate, span) } } diff --git a/src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs new file mode 100644 index 000000000000..3bb2480e9e2b --- /dev/null +++ b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.rs @@ -0,0 +1,29 @@ +// Check that supertraits cannot be used to work around min_specialization +// limitations. + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +trait HasMethod { + fn method(&self); +} + +#[rustc_unsafe_specialization_marker] +trait Marker: HasMethod {} + +trait Spec { + fn spec_me(&self); +} + +impl Spec for T { + default fn spec_me(&self) {} +} + +impl Spec for T { + //~^ ERROR cannot specialize on trait `HasMethod` + fn spec_me(&self) { + self.method(); + } +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr new file mode 100644 index 000000000000..964109dd10f4 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr @@ -0,0 +1,13 @@ +error: cannot specialize on trait `HasMethod` + --> $DIR/spec-marker-supertraits.rs:22:1 + | +LL | / impl Spec for T { +LL | | +LL | | fn spec_me(&self) { +LL | | self.method(); +LL | | } +LL | | } + | |_^ + +error: aborting due to previous error +