Require ~const
qualifier on trait bounds in specializing impls if present in base impl.
This commit is contained in:
parent
d492b9b000
commit
c0ae62ee95
8 changed files with 137 additions and 24 deletions
|
@ -391,7 +391,7 @@ fn check_predicates<'tcx>(
|
||||||
);
|
);
|
||||||
|
|
||||||
for (predicate, span) in impl1_predicates {
|
for (predicate, span) in impl1_predicates {
|
||||||
if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(predicate, *pred2)) {
|
if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) {
|
||||||
check_specialization_on(tcx, predicate, span)
|
check_specialization_on(tcx, predicate, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,8 +400,8 @@ fn check_predicates<'tcx>(
|
||||||
/// Checks if some predicate on the specializing impl (`predicate1`) is the same
|
/// Checks if some predicate on the specializing impl (`predicate1`) is the same
|
||||||
/// as some predicate on the base impl (`predicate2`).
|
/// as some predicate on the base impl (`predicate2`).
|
||||||
///
|
///
|
||||||
/// This is slightly more complicated than simple syntactic equivalence, since
|
/// This basically just checks syntactic equivalence, but is a little more
|
||||||
/// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
|
/// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work:
|
||||||
///
|
///
|
||||||
/// ```ignore (illustrative)
|
/// ```ignore (illustrative)
|
||||||
/// #[rustc_specialization_trait]
|
/// #[rustc_specialization_trait]
|
||||||
|
@ -410,27 +410,54 @@ fn check_predicates<'tcx>(
|
||||||
/// impl<T: Bound> Tr for T { }
|
/// impl<T: Bound> Tr for T { }
|
||||||
/// impl<T: ~const Bound + Specialize> const Tr for T { }
|
/// impl<T: ~const Bound + Specialize> const Tr for T { }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// However, we *don't* want to allow the reverse, i.e., when the bound on the
|
||||||
|
/// specializing impl is not as const as the bound on the base impl:
|
||||||
|
///
|
||||||
|
/// ```ignore (illustrative)
|
||||||
|
/// impl<T: ~const Bound> const Tr for T { }
|
||||||
|
/// impl<T: Bound + Specialize> const Tr for T { } // should be T: ~const Bound
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// So we make that check in this function and try to raise a helpful error message.
|
||||||
fn trait_predicates_eq<'tcx>(
|
fn trait_predicates_eq<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
predicate1: ty::Predicate<'tcx>,
|
predicate1: ty::Predicate<'tcx>,
|
||||||
predicate2: ty::Predicate<'tcx>,
|
predicate2: ty::Predicate<'tcx>,
|
||||||
|
span: Span,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let predicate_kind_without_constness = |kind: ty::PredicateKind<'tcx>| match kind {
|
let pred1_kind = predicate1.kind().no_bound_vars();
|
||||||
ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity }) => {
|
let pred2_kind = predicate2.kind().no_bound_vars();
|
||||||
ty::PredicateKind::Trait(ty::TraitPredicate {
|
let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) {
|
||||||
trait_ref,
|
(Some(ty::PredicateKind::Trait(pred1)), Some(ty::PredicateKind::Trait(pred2))) => {
|
||||||
constness: ty::BoundConstness::NotConst,
|
(pred1, pred2)
|
||||||
polarity,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
_ => kind,
|
// Just use plain syntactic equivalence if either of the predicates aren't
|
||||||
|
// trait predicates or have bound vars.
|
||||||
|
_ => return pred1_kind == pred2_kind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// We rely on `check_constness` above to ensure that pred1 is const if pred2
|
let predicates_equal_modulo_constness = {
|
||||||
// is const.
|
let pred1_unconsted =
|
||||||
let pred1_kind_not_const = predicate1.kind().map_bound(predicate_kind_without_constness);
|
ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 };
|
||||||
let pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness);
|
let pred2_unconsted =
|
||||||
|
ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 };
|
||||||
|
pred1_unconsted == pred2_unconsted
|
||||||
|
};
|
||||||
|
|
||||||
pred1_kind_not_const == pred2_kind_not_const
|
if !predicates_equal_modulo_constness {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the predicate on the specializing impl is at least as const as
|
||||||
|
// the one on the base.
|
||||||
|
if trait_pred2.constness == ty::BoundConstness::ConstIfConst
|
||||||
|
&& trait_pred1.constness == ty::BoundConstness::NotConst
|
||||||
|
{
|
||||||
|
tcx.sess.struct_span_err(span, "missing `~const` qualifier").emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(tcx))]
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Tests that trait bounds on specializing trait impls must be `~const` if the
|
||||||
|
// same bound is present on the default impl and is `~const` there.
|
||||||
|
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
|
||||||
|
#[rustc_specialization_trait]
|
||||||
|
trait Specialize {}
|
||||||
|
|
||||||
|
#[const_trait]
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
#[const_trait]
|
||||||
|
trait Bar {}
|
||||||
|
|
||||||
|
// bgr360: I was only able to exercise the code path that raises the
|
||||||
|
// "missing ~const qualifier" error by making this base impl non-const, even
|
||||||
|
// though that doesn't really make sense to do. As seen below, if the base impl
|
||||||
|
// is made const, rustc fails earlier with an overlapping impl failure.
|
||||||
|
impl<T> Bar for T
|
||||||
|
where
|
||||||
|
T: ~const Foo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
impl<T> Bar for T
|
||||||
|
where
|
||||||
|
T: Foo, //~ ERROR missing `~const` qualifier
|
||||||
|
T: Specialize,
|
||||||
|
{}
|
||||||
|
|
||||||
|
#[const_trait]
|
||||||
|
trait Baz {}
|
||||||
|
|
||||||
|
impl<T> const Baz for T
|
||||||
|
where
|
||||||
|
T: ~const Foo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
impl<T> const Baz for T //~ ERROR conflicting implementations of trait `Baz`
|
||||||
|
where
|
||||||
|
T: Foo,
|
||||||
|
T: Specialize,
|
||||||
|
{}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,18 @@
|
||||||
|
error: missing `~const` qualifier
|
||||||
|
--> $DIR/const-default-bound-non-const-specialized-bound.rs:28:8
|
||||||
|
|
|
||||||
|
LL | T: Foo,
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error[E0119]: conflicting implementations of trait `Baz`
|
||||||
|
--> $DIR/const-default-bound-non-const-specialized-bound.rs:40:1
|
||||||
|
|
|
||||||
|
LL | impl<T> const Baz for T
|
||||||
|
| ----------------------- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T> const Baz for T
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -1,5 +1,4 @@
|
||||||
// Tests that a const default trait impl cannot be specialized by a non-const
|
// Tests that specializing trait impls must be at least as const as the default impl.
|
||||||
// trait impl.
|
|
||||||
|
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
|
@ -1,5 +1,5 @@
|
||||||
error: cannot specialize on const impl with non-const impl
|
error: cannot specialize on const impl with non-const impl
|
||||||
--> $DIR/const-default-non-const-specialized.rs:20:1
|
--> $DIR/const-default-impl-non-const-specialized-impl.rs:19:1
|
||||||
|
|
|
|
||||||
LL | impl Value for FortyTwo {
|
LL | impl Value for FortyTwo {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
|
@ -1,5 +1,6 @@
|
||||||
// Tests that `T: Foo` and `T: ~const Foo` are treated as equivalent for the
|
// Tests that `T: ~const Foo` in a specializing impl is treated as equivalent to
|
||||||
// purposes of min_specialization.
|
// `T: Foo` in the default impl for the purposes of specialization (i.e., it
|
||||||
|
// does not think that the user is attempting to specialize on trait `Foo`).
|
||||||
|
|
||||||
// check-pass
|
// check-pass
|
||||||
|
|
||||||
|
@ -27,4 +28,18 @@ where
|
||||||
T: Specialize,
|
T: Specialize,
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
#[const_trait]
|
||||||
|
trait Baz {}
|
||||||
|
|
||||||
|
impl<T> const Baz for T
|
||||||
|
where
|
||||||
|
T: Foo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
impl<T> const Baz for T
|
||||||
|
where
|
||||||
|
T: ~const Foo,
|
||||||
|
T: Specialize,
|
||||||
|
{}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -17,7 +17,9 @@ impl<T: ~const Default> const A for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default + Sup> A for T { //~ ERROR: cannot specialize
|
impl<T: Default + Sup> A for T {
|
||||||
|
//~^ ERROR: cannot specialize
|
||||||
|
//~| ERROR: missing `~const` qualifier
|
||||||
fn a() -> u32 {
|
fn a() -> u32 {
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
error: cannot specialize on trait `Default`
|
error: cannot specialize on const impl with non-const impl
|
||||||
|
--> $DIR/specializing-constness.rs:20:1
|
||||||
|
|
|
||||||
|
LL | impl<T: Default + Sup> A for T {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: missing `~const` qualifier
|
||||||
--> $DIR/specializing-constness.rs:20:9
|
--> $DIR/specializing-constness.rs:20:9
|
||||||
|
|
|
|
||||||
LL | impl<T: Default + Sup> A for T {
|
LL | impl<T: Default + Sup> A for T {
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue