Elaborate predicates in min_specialization checks

Supertraits of specialization markers could circumvent checks for
min_specialization. Elaborating predicates prevents this.
This commit is contained in:
Matthew Jasper 2021-09-30 21:42:09 +01:00
parent 6dc08b909b
commit c8f86cad2d
4 changed files with 76 additions and 18 deletions

View file

@ -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,

View file

@ -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<D: Debug> 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)
}
}

View file

@ -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<T> Spec for T {
default fn spec_me(&self) {}
}
impl<T: Marker> Spec for T {
//~^ ERROR cannot specialize on trait `HasMethod`
fn spec_me(&self) {
self.method();
}
}
fn main() {}

View file

@ -0,0 +1,13 @@
error: cannot specialize on trait `HasMethod`
--> $DIR/spec-marker-supertraits.rs:22:1
|
LL | / impl<T: Marker> Spec for T {
LL | |
LL | | fn spec_me(&self) {
LL | | self.method();
LL | | }
LL | | }
| |_^
error: aborting due to previous error