parent
01a6f30324
commit
5c25d30f6f
9 changed files with 286 additions and 21 deletions
|
@ -80,6 +80,7 @@ use rustc_span::Span;
|
||||||
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
|
||||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt};
|
use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt};
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
|
pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
|
||||||
if let Some(node) = parent_specialization_node(tcx, impl_def_id) {
|
if let Some(node) = parent_specialization_node(tcx, impl_def_id) {
|
||||||
|
@ -103,13 +104,11 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that `impl1` is a sound specialization
|
/// Check that `impl1` is a sound specialization
|
||||||
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) {
|
fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) {
|
||||||
if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
|
if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
|
||||||
let impl2_def_id = impl2_node.def_id();
|
let impl2_def_id = impl2_node.def_id();
|
||||||
debug!(
|
debug!(?impl2_def_id, ?impl2_substs);
|
||||||
"check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)",
|
|
||||||
impl1_def_id, impl2_def_id, impl2_substs
|
|
||||||
);
|
|
||||||
|
|
||||||
let parent_substs = if impl2_node.is_from_trait() {
|
let parent_substs = if impl2_node.is_from_trait() {
|
||||||
impl2_substs.to_vec()
|
impl2_substs.to_vec()
|
||||||
|
@ -280,13 +279,13 @@ fn check_static_lifetimes<'tcx>(
|
||||||
///
|
///
|
||||||
/// Each predicate `P` must be:
|
/// Each predicate `P` must be:
|
||||||
///
|
///
|
||||||
/// * global (not reference any parameters)
|
/// * Global (not reference any parameters).
|
||||||
/// * `T: Tr` predicate where `Tr` is an always-applicable trait
|
/// * A `T: Tr` predicate where `Tr` is an always-applicable trait.
|
||||||
/// * on the base `impl impl2`
|
/// * Present on the base impl `impl2`.
|
||||||
/// * Currently this check is done using syntactic equality, which is
|
/// * This check is done using the `trait_predicates_eq` function below.
|
||||||
/// conservative but generally sufficient.
|
/// * A well-formed predicate of a type argument of the trait being implemented,
|
||||||
/// * a well-formed predicate of a type argument of the trait being implemented,
|
|
||||||
/// including the `Self`-type.
|
/// including the `Self`-type.
|
||||||
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
fn check_predicates<'tcx>(
|
fn check_predicates<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
impl1_def_id: LocalDefId,
|
impl1_def_id: LocalDefId,
|
||||||
|
@ -322,10 +321,7 @@ fn check_predicates<'tcx>(
|
||||||
.map(|obligation| obligation.predicate)
|
.map(|obligation| obligation.predicate)
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
debug!(
|
debug!(?impl1_predicates, ?impl2_predicates);
|
||||||
"check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)",
|
|
||||||
impl1_predicates, impl2_predicates,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Since impls of always applicable traits don't get to assume anything, we
|
// Since impls of always applicable traits don't get to assume anything, we
|
||||||
// can also assume their supertraits apply.
|
// can also assume their supertraits apply.
|
||||||
|
@ -373,25 +369,52 @@ fn check_predicates<'tcx>(
|
||||||
);
|
);
|
||||||
|
|
||||||
for (predicate, span) in impl1_predicates {
|
for (predicate, span) in impl1_predicates {
|
||||||
if !impl2_predicates.contains(&predicate) {
|
if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(predicate, *pred2)) {
|
||||||
check_specialization_on(tcx, predicate, span)
|
check_specialization_on(tcx, predicate, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether two predicates are the same for the purposes of specialization.
|
||||||
|
///
|
||||||
|
/// This is slightly more complicated than simple syntactic equivalence, since
|
||||||
|
/// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
|
||||||
|
///
|
||||||
|
/// #[rustc_specialization_trait]
|
||||||
|
/// trait Specialize { }
|
||||||
|
///
|
||||||
|
/// impl<T: ~const Bound> const Tr for T { }
|
||||||
|
/// impl<T: Bound + Specialize> Tr for T { }
|
||||||
|
fn trait_predicates_eq<'tcx>(
|
||||||
|
predicate1: ty::Predicate<'tcx>,
|
||||||
|
predicate2: ty::Predicate<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
let predicate_kind_without_constness = |kind: ty::PredicateKind<'tcx>| match kind {
|
||||||
|
ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity }) => {
|
||||||
|
ty::PredicateKind::Trait(ty::TraitPredicate {
|
||||||
|
trait_ref,
|
||||||
|
constness: ty::BoundConstness::NotConst,
|
||||||
|
polarity,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => kind,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pred1_kind_not_const = predicate1.kind().map_bound(predicate_kind_without_constness);
|
||||||
|
let pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness);
|
||||||
|
|
||||||
|
pred1_kind_not_const == pred2_kind_not_const
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) {
|
fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) {
|
||||||
debug!("can_specialize_on(predicate = {:?})", predicate);
|
|
||||||
match predicate.kind().skip_binder() {
|
match predicate.kind().skip_binder() {
|
||||||
// Global predicates are either always true or always false, so we
|
// Global predicates are either always true or always false, so we
|
||||||
// are fine to specialize on.
|
// are fine to specialize on.
|
||||||
_ if predicate.is_global() => (),
|
_ if predicate.is_global() => (),
|
||||||
// We allow specializing on explicitly marked traits with no associated
|
// We allow specializing on explicitly marked traits with no associated
|
||||||
// items.
|
// items.
|
||||||
ty::PredicateKind::Trait(ty::TraitPredicate {
|
ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => {
|
||||||
trait_ref,
|
|
||||||
constness: ty::BoundConstness::NotConst,
|
|
||||||
polarity: _,
|
|
||||||
}) => {
|
|
||||||
if !matches!(
|
if !matches!(
|
||||||
trait_predicate_kind(tcx, predicate),
|
trait_predicate_kind(tcx, predicate),
|
||||||
Some(TraitSpecializationKind::Marker)
|
Some(TraitSpecializationKind::Marker)
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Tests that a const default trait impl can be specialized by another const
|
||||||
|
// trait impl and that the specializing impl will be used during const-eval.
|
||||||
|
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
|
||||||
|
trait Value {
|
||||||
|
fn value() -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn get_value<T: ~const Value>() -> u32 {
|
||||||
|
T::value()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> const Value for T {
|
||||||
|
default fn value() -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FortyTwo;
|
||||||
|
|
||||||
|
impl const Value for FortyTwo {
|
||||||
|
fn value() -> u32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZERO: u32 = get_value::<()>();
|
||||||
|
|
||||||
|
const FORTY_TWO: u32 = get_value::<FortyTwo>();
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(ZERO, 0);
|
||||||
|
assert_eq!(FORTY_TWO, 42);
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Tests that a const default trait impl can be specialized by a non-const trait
|
||||||
|
// impl, but that the specializing impl cannot be used in a const context.
|
||||||
|
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
|
||||||
|
trait Value {
|
||||||
|
fn value() -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn get_value<T: ~const Value>() -> u32 {
|
||||||
|
T::value()
|
||||||
|
//~^ ERROR any use of this value will cause an error [const_err]
|
||||||
|
//~| WARNING this was previously accepted
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> const Value for T {
|
||||||
|
default fn value() -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FortyTwo;
|
||||||
|
|
||||||
|
impl Value for FortyTwo {
|
||||||
|
fn value() -> u32 {
|
||||||
|
println!("You can't do that (constly)");
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZERO: u32 = get_value::<()>();
|
||||||
|
|
||||||
|
const FORTY_TWO: u32 =
|
||||||
|
get_value::<FortyTwo>(); // This is the line that causes the error, but it gets reported above
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,37 @@
|
||||||
|
error: any use of this value will cause an error
|
||||||
|
--> $DIR/const-default-non-const-specialized.rs:12:5
|
||||||
|
|
|
||||||
|
LL | T::value()
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| calling non-const function `<FortyTwo as Value>::value`
|
||||||
|
| inside `get_value::<FortyTwo>` at $DIR/const-default-non-const-specialized.rs:12:5
|
||||||
|
| inside `FORTY_TWO` at $DIR/const-default-non-const-specialized.rs:35:5
|
||||||
|
...
|
||||||
|
LL | const FORTY_TWO: u32 =
|
||||||
|
| --------------------
|
||||||
|
|
|
||||||
|
= note: `#[deny(const_err)]` on by default
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Future incompatibility report: Future breakage diagnostic:
|
||||||
|
error: any use of this value will cause an error
|
||||||
|
--> $DIR/const-default-non-const-specialized.rs:12:5
|
||||||
|
|
|
||||||
|
LL | T::value()
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| calling non-const function `<FortyTwo as Value>::value`
|
||||||
|
| inside `get_value::<FortyTwo>` at $DIR/const-default-non-const-specialized.rs:12:5
|
||||||
|
| inside `FORTY_TWO` at $DIR/const-default-non-const-specialized.rs:35:5
|
||||||
|
...
|
||||||
|
LL | const FORTY_TWO: u32 =
|
||||||
|
| --------------------
|
||||||
|
|
|
||||||
|
= note: `#[deny(const_err)]` on by default
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
|
||||||
|
trait Foo {
|
||||||
|
fn foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl const Foo for u32 {
|
||||||
|
default fn foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Tests that `~const` trait bounds can be used to specialize const trait impls.
|
||||||
|
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
|
||||||
|
#[rustc_specialization_trait]
|
||||||
|
trait Specialize {}
|
||||||
|
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
impl<T> const Foo for T {}
|
||||||
|
|
||||||
|
impl<T> const Foo for T
|
||||||
|
where
|
||||||
|
T: ~const Specialize,
|
||||||
|
{}
|
||||||
|
|
||||||
|
trait Bar {}
|
||||||
|
|
||||||
|
impl<T> const Bar for T
|
||||||
|
where
|
||||||
|
T: ~const Foo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
impl<T> const Bar for T
|
||||||
|
where
|
||||||
|
T: ~const Foo,
|
||||||
|
T: ~const Specialize,
|
||||||
|
{}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Tests that `T: ~const Foo` and `T: Foo` are treated as equivalent for the
|
||||||
|
// purposes of min_specialization.
|
||||||
|
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
|
||||||
|
#[rustc_specialization_trait]
|
||||||
|
trait Specialize {}
|
||||||
|
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
trait Bar {}
|
||||||
|
|
||||||
|
impl<T> const Bar for T
|
||||||
|
where
|
||||||
|
T: ~const Foo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
impl<T> Bar for T
|
||||||
|
where
|
||||||
|
T: Foo,
|
||||||
|
T: Specialize,
|
||||||
|
{}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Tests that a non-const default impl can be specialized by a const trait impl,
|
||||||
|
// but that the default impl cannot be used in a const context.
|
||||||
|
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
|
||||||
|
trait Value {
|
||||||
|
fn value() -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn get_value<T: ~const Value>() -> u32 {
|
||||||
|
T::value()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Value for T {
|
||||||
|
default fn value() -> u32 {
|
||||||
|
println!("You can't do that (constly)");
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FortyTwo;
|
||||||
|
|
||||||
|
impl const Value for FortyTwo {
|
||||||
|
fn value() -> u32 {
|
||||||
|
42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZERO: u32 = get_value::<()>(); //~ ERROR the trait bound `(): ~const Value` is not satisfied
|
||||||
|
|
||||||
|
const FORTY_TWO: u32 = get_value::<FortyTwo>();
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,20 @@
|
||||||
|
error[E0277]: the trait bound `(): ~const Value` is not satisfied
|
||||||
|
--> $DIR/non-const-default-const-specialized.rs:30:31
|
||||||
|
|
|
||||||
|
LL | const ZERO: u32 = get_value::<()>();
|
||||||
|
| ^^ the trait `~const Value` is not implemented for `()`
|
||||||
|
|
|
||||||
|
note: the trait `Value` is implemented for `()`, but that implementation is not `const`
|
||||||
|
--> $DIR/non-const-default-const-specialized.rs:30:31
|
||||||
|
|
|
||||||
|
LL | const ZERO: u32 = get_value::<()>();
|
||||||
|
| ^^
|
||||||
|
note: required by a bound in `get_value`
|
||||||
|
--> $DIR/non-const-default-const-specialized.rs:11:23
|
||||||
|
|
|
||||||
|
LL | const fn get_value<T: ~const Value>() -> u32 {
|
||||||
|
| ^^^^^^^^^^^^ required by this bound in `get_value`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Add table
Add a link
Reference in a new issue