Disallow specializing on const impls with non-const impls.
This commit is contained in:
parent
5c25d30f6f
commit
ce03d259da
6 changed files with 52 additions and 81 deletions
|
@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp;
|
||||||
use crate::errors::SubstsOnOverriddenImpl;
|
use crate::errors::SubstsOnOverriddenImpl;
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
@ -117,12 +118,33 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
|
||||||
};
|
};
|
||||||
|
|
||||||
let span = tcx.def_span(impl1_def_id);
|
let span = tcx.def_span(impl1_def_id);
|
||||||
|
check_constness(tcx, impl1_def_id, impl2_node, span);
|
||||||
check_static_lifetimes(tcx, &parent_substs, span);
|
check_static_lifetimes(tcx, &parent_substs, span);
|
||||||
check_duplicate_params(tcx, impl1_substs, &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(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that the specializing impl `impl1` is at least as const as the base
|
||||||
|
/// impl `impl2`
|
||||||
|
fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node, span: Span) {
|
||||||
|
if impl2_node.is_from_trait() {
|
||||||
|
// This isn't a specialization
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let impl1_constness = tcx.constness(impl1_def_id.to_def_id());
|
||||||
|
let impl2_constness = tcx.constness(impl2_node.def_id());
|
||||||
|
|
||||||
|
if let hir::Constness::Const = impl2_constness {
|
||||||
|
if let hir::Constness::NotConst = impl1_constness {
|
||||||
|
tcx.sess
|
||||||
|
.struct_span_err(span, "cannot specialize on const impl with non-const impl")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
|
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
|
||||||
/// substitutions `(S1, S2)` that equate their trait references. The returned
|
/// substitutions `(S1, S2)` that equate their trait references. The returned
|
||||||
/// types are expressed in terms of the generics of `impl1`.
|
/// types are expressed in terms of the generics of `impl1`.
|
||||||
|
@ -277,7 +299,7 @@ fn check_static_lifetimes<'tcx>(
|
||||||
|
|
||||||
/// Check whether predicates on the specializing impl (`impl1`) are allowed.
|
/// Check whether predicates on the specializing impl (`impl1`) are allowed.
|
||||||
///
|
///
|
||||||
/// Each predicate `P` must be:
|
/// Each predicate `P` must be one of:
|
||||||
///
|
///
|
||||||
/// * Global (not reference any parameters).
|
/// * Global (not reference any parameters).
|
||||||
/// * A `T: Tr` predicate where `Tr` is an always-applicable trait.
|
/// * A `T: Tr` predicate where `Tr` is an always-applicable trait.
|
||||||
|
@ -375,16 +397,19 @@ fn check_predicates<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether two predicates are the same for the purposes of specialization.
|
/// Checks if some predicate on the specializing impl (`predicate1`) is the same
|
||||||
|
/// as some predicate on the base impl (`predicate2`).
|
||||||
///
|
///
|
||||||
/// This is slightly more complicated than simple syntactic equivalence, since
|
/// This is slightly more complicated than simple syntactic equivalence, since
|
||||||
/// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
|
/// we want to equate `T: Tr` with `T: ~const Tr` so this can work:
|
||||||
///
|
///
|
||||||
|
/// ```ignore (illustrative)
|
||||||
/// #[rustc_specialization_trait]
|
/// #[rustc_specialization_trait]
|
||||||
/// trait Specialize { }
|
/// trait Specialize { }
|
||||||
///
|
///
|
||||||
/// impl<T: ~const Bound> const Tr for T { }
|
/// impl<T: Bound> Tr for T { }
|
||||||
/// impl<T: Bound + Specialize> Tr for T { }
|
/// impl<T: ~const Bound + Specialize> const Tr for T { }
|
||||||
|
/// ```
|
||||||
fn trait_predicates_eq<'tcx>(
|
fn trait_predicates_eq<'tcx>(
|
||||||
predicate1: ty::Predicate<'tcx>,
|
predicate1: ty::Predicate<'tcx>,
|
||||||
predicate2: ty::Predicate<'tcx>,
|
predicate2: ty::Predicate<'tcx>,
|
||||||
|
@ -400,6 +425,8 @@ fn trait_predicates_eq<'tcx>(
|
||||||
_ => kind,
|
_ => kind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// We rely on `check_constness` above to ensure that pred1 is const if pred2
|
||||||
|
// is const.
|
||||||
let pred1_kind_not_const = predicate1.kind().map_bound(predicate_kind_without_constness);
|
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);
|
let pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Tests that a const default trait impl can be specialized by a non-const trait
|
// Tests that a const default trait impl cannot be specialized by a non-const
|
||||||
// impl, but that the specializing impl cannot be used in a const context.
|
// trait impl.
|
||||||
|
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
|
@ -8,12 +8,6 @@ trait Value {
|
||||||
fn value() -> u32;
|
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 {
|
impl<T> const Value for T {
|
||||||
default fn value() -> u32 {
|
default fn value() -> u32 {
|
||||||
0
|
0
|
||||||
|
@ -22,16 +16,11 @@ impl<T> const Value for T {
|
||||||
|
|
||||||
struct FortyTwo;
|
struct FortyTwo;
|
||||||
|
|
||||||
impl Value for FortyTwo {
|
impl Value for FortyTwo { //~ ERROR cannot specialize on const impl with non-const impl
|
||||||
fn value() -> u32 {
|
fn value() -> u32 {
|
||||||
println!("You can't do that (constly)");
|
println!("You can't do that (constly)");
|
||||||
42
|
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() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,37 +1,8 @@
|
||||||
error: any use of this value will cause an error
|
error: cannot specialize on const impl with non-const impl
|
||||||
--> $DIR/const-default-non-const-specialized.rs:12:5
|
--> $DIR/const-default-non-const-specialized.rs:19:1
|
||||||
|
|
|
|
||||||
LL | T::value()
|
LL | impl Value for FortyTwo {
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
| |
|
|
||||||
| 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
|
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>
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Tests that `T: ~const Foo` and `T: Foo` are treated as equivalent for the
|
// Tests that `T: Foo` and `T: ~const Foo` are treated as equivalent for the
|
||||||
// purposes of min_specialization.
|
// purposes of min_specialization.
|
||||||
|
|
||||||
// check-pass
|
// check-pass
|
||||||
|
@ -14,14 +14,14 @@ trait Foo {}
|
||||||
|
|
||||||
trait Bar {}
|
trait Bar {}
|
||||||
|
|
||||||
impl<T> const Bar for T
|
|
||||||
where
|
|
||||||
T: ~const Foo,
|
|
||||||
{}
|
|
||||||
|
|
||||||
impl<T> Bar for T
|
impl<T> Bar for T
|
||||||
where
|
where
|
||||||
T: Foo,
|
T: Foo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
impl<T> const Bar for T
|
||||||
|
where
|
||||||
|
T: ~const Foo,
|
||||||
T: Specialize,
|
T: Specialize,
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Tests that a non-const default impl can be specialized by a const trait impl,
|
// 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.
|
// but that the default impl cannot be used in a const context.
|
||||||
|
|
||||||
|
// run-pass
|
||||||
|
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
|
|
||||||
|
@ -27,8 +29,10 @@ impl const Value for FortyTwo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ZERO: u32 = get_value::<()>(); //~ ERROR the trait bound `(): ~const Value` is not satisfied
|
fn main() {
|
||||||
|
let zero = get_value::<()>();
|
||||||
|
assert_eq!(zero, 0);
|
||||||
|
|
||||||
const FORTY_TWO: u32 = get_value::<FortyTwo>();
|
const FORTY_TWO: u32 = get_value::<FortyTwo>();
|
||||||
|
assert_eq!(FORTY_TWO, 42);
|
||||||
fn main() {}
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
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