Fix const specialization
This commit is contained in:
parent
e91fc1bc0c
commit
9bda88bb58
11 changed files with 133 additions and 50 deletions
|
@ -113,8 +113,6 @@ hir_analysis_const_param_ty_impl_on_unsized =
|
||||||
the trait `ConstParamTy` may not be implemented for this type
|
the trait `ConstParamTy` may not be implemented for this type
|
||||||
.label = type is not `Sized`
|
.label = type is not `Sized`
|
||||||
|
|
||||||
hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
|
|
||||||
|
|
||||||
hir_analysis_copy_impl_on_non_adt =
|
hir_analysis_copy_impl_on_non_adt =
|
||||||
the trait `Copy` cannot be implemented for this type
|
the trait `Copy` cannot be implemented for this type
|
||||||
.label = type is not a structure or enumeration
|
.label = type is not a structure or enumeration
|
||||||
|
|
|
@ -1079,13 +1079,6 @@ pub(crate) struct EmptySpecialization {
|
||||||
pub base_impl_span: Span,
|
pub base_impl_span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
|
||||||
#[diag(hir_analysis_const_specialize)]
|
|
||||||
pub(crate) struct ConstSpecialize {
|
|
||||||
#[primary_span]
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(hir_analysis_static_specialize)]
|
#[diag(hir_analysis_static_specialize)]
|
||||||
pub(crate) struct StaticSpecialize {
|
pub(crate) struct StaticSpecialize {
|
||||||
|
|
|
@ -66,7 +66,6 @@
|
||||||
//! on traits with methods can.
|
//! on traits with methods can.
|
||||||
|
|
||||||
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::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||||
|
@ -134,7 +133,6 @@ fn check_always_applicable(
|
||||||
unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args)
|
unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args)
|
||||||
};
|
};
|
||||||
|
|
||||||
res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span));
|
|
||||||
res = res.and(check_static_lifetimes(tcx, &parent_args, span));
|
res = res.and(check_static_lifetimes(tcx, &parent_args, span));
|
||||||
res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
|
res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
|
||||||
res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
|
res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
|
||||||
|
@ -157,30 +155,6 @@ fn check_has_items(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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,
|
|
||||||
) -> Result<(), ErrorGuaranteed> {
|
|
||||||
if impl2_node.is_from_trait() {
|
|
||||||
// This isn't a specialization
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return Err(tcx.dcx().emit_err(errors::ConstSpecialize { span }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
|
/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two
|
||||||
/// generic parameters `(S1, S2)` that equate their trait references.
|
/// generic parameters `(S1, S2)` that equate their trait references.
|
||||||
/// The returned types are expressed in terms of the generics of `impl1`.
|
/// The returned types are expressed in terms of the generics of `impl1`.
|
||||||
|
|
|
@ -225,11 +225,17 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool
|
||||||
tcx.features().specialization() || tcx.features().min_specialization()
|
tcx.features().specialization() || tcx.features().min_specialization()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is `impl1` a specialization of `impl2`?
|
/// Is `specializing_impl_def_id` a specialization of `parent_impl_def_id`?
|
||||||
///
|
///
|
||||||
/// Specialization is determined by the sets of types to which the impls apply;
|
/// For every type that could apply to `specializing_impl_def_id`, we prove that
|
||||||
/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
|
/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and
|
||||||
/// to.
|
/// its where-clauses hold).
|
||||||
|
///
|
||||||
|
/// For the purposes of const traits, we also check that the specializing
|
||||||
|
/// impl is not more restrictive than the parent impl. That is, if the
|
||||||
|
/// `parent_impl_def_id` is a const impl (conditionally based off of some `~const`
|
||||||
|
/// bounds), then `specializing_impl_def_id` must also be const for the same
|
||||||
|
/// set of types.
|
||||||
#[instrument(skip(tcx), level = "debug")]
|
#[instrument(skip(tcx), level = "debug")]
|
||||||
pub(super) fn specializes(
|
pub(super) fn specializes(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
|
@ -339,8 +345,14 @@ pub(super) fn specializes(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the parent impl is const, then the specializing impl must be const.
|
// If the parent impl is const, then the specializing impl must be const,
|
||||||
|
// and it must not be *more restrictive* than the parent impl (that is,
|
||||||
|
// it cannot be const in fewer cases than the parent impl).
|
||||||
if tcx.is_conditionally_const(parent_impl_def_id) {
|
if tcx.is_conditionally_const(parent_impl_def_id) {
|
||||||
|
if !tcx.is_conditionally_const(specializing_impl_def_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let const_conditions = ocx.normalize(
|
let const_conditions = ocx.normalize(
|
||||||
cause,
|
cause,
|
||||||
param_env,
|
param_env,
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `Foo` for type `(_,)`
|
||||||
|
--> $DIR/overlap-const-with-nonconst.rs:23:1
|
||||||
|
|
|
||||||
|
LL | / impl<T> const Foo for T
|
||||||
|
LL | | where
|
||||||
|
LL | | T: ~const Bar,
|
||||||
|
| |__________________- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T> Foo for (T,) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_,)`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
38
tests/ui/traits/const-traits/overlap-const-with-nonconst.rs
Normal file
38
tests/ui/traits/const-traits/overlap-const-with-nonconst.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//@ revisions: spec min_spec
|
||||||
|
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![cfg_attr(spec, feature(specialization))]
|
||||||
|
//[spec]~^ WARN the feature `specialization` is incomplete
|
||||||
|
#![cfg_attr(min_spec, feature(min_specialization))]
|
||||||
|
|
||||||
|
#[const_trait]
|
||||||
|
trait Bar {}
|
||||||
|
impl<T> const Bar for T {}
|
||||||
|
|
||||||
|
#[const_trait]
|
||||||
|
trait Foo {
|
||||||
|
fn method(&self);
|
||||||
|
}
|
||||||
|
impl<T> const Foo for T
|
||||||
|
where
|
||||||
|
T: ~const Bar,
|
||||||
|
{
|
||||||
|
default fn method(&self) {}
|
||||||
|
}
|
||||||
|
// specializing impl:
|
||||||
|
impl<T> Foo for (T,) {
|
||||||
|
//~^ ERROR conflicting implementations
|
||||||
|
fn method(&self) {
|
||||||
|
println!("hi");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn dispatch<T: ~const Bar + Copy>(t: T) {
|
||||||
|
t.method();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
const {
|
||||||
|
dispatch(((),));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/overlap-const-with-nonconst.rs:4:27
|
||||||
|
|
|
||||||
|
LL | #![cfg_attr(spec, feature(specialization))]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
|
||||||
|
= help: consider using `min_specialization` instead, which is more stable and complete
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error[E0119]: conflicting implementations of trait `Foo` for type `(_,)`
|
||||||
|
--> $DIR/overlap-const-with-nonconst.rs:23:1
|
||||||
|
|
|
||||||
|
LL | / impl<T> const Foo for T
|
||||||
|
LL | | where
|
||||||
|
LL | | T: ~const Bar,
|
||||||
|
| |__________________- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl<T> Foo for (T,) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_,)`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0119]: conflicting implementations of trait `Value` for type `FortyTwo`
|
||||||
|
--> $DIR/const-default-impl-non-const-specialized-impl.rs:22:1
|
||||||
|
|
|
||||||
|
LL | impl<T> const Value for T {
|
||||||
|
| ------------------------- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl Value for FortyTwo {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `FortyTwo`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -1,7 +1,10 @@
|
||||||
// Tests that specializing trait impls must be at least as const as the default impl.
|
// Tests that specializing trait impls must be at least as const as the default impl.
|
||||||
|
//@ revisions: spec min_spec
|
||||||
|
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(min_specialization)]
|
#![cfg_attr(spec, feature(specialization))]
|
||||||
|
//[spec]~^ WARN the feature `specialization` is incomplete
|
||||||
|
#![cfg_attr(min_spec, feature(min_specialization))]
|
||||||
|
|
||||||
#[const_trait]
|
#[const_trait]
|
||||||
trait Value {
|
trait Value {
|
||||||
|
@ -16,7 +19,8 @@ impl<T> const Value for T {
|
||||||
|
|
||||||
struct FortyTwo;
|
struct FortyTwo;
|
||||||
|
|
||||||
impl Value for FortyTwo { //~ ERROR cannot specialize on const impl with non-const impl
|
impl Value for FortyTwo {
|
||||||
|
//~^ ERROR conflicting implementations
|
||||||
fn value() -> u32 {
|
fn value() -> u32 {
|
||||||
println!("You can't do that (constly)");
|
println!("You can't do that (constly)");
|
||||||
42
|
42
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/const-default-impl-non-const-specialized-impl.rs:5:27
|
||||||
|
|
|
||||||
|
LL | #![cfg_attr(spec, feature(specialization))]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
|
||||||
|
= help: consider using `min_specialization` instead, which is more stable and complete
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error[E0119]: conflicting implementations of trait `Value` for type `FortyTwo`
|
||||||
|
--> $DIR/const-default-impl-non-const-specialized-impl.rs:22:1
|
||||||
|
|
|
||||||
|
LL | impl<T> const Value for T {
|
||||||
|
| ------------------------- first implementation here
|
||||||
|
...
|
||||||
|
LL | impl Value for FortyTwo {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `FortyTwo`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0119`.
|
|
@ -1,8 +0,0 @@
|
||||||
error: cannot specialize on const impl with non-const impl
|
|
||||||
--> $DIR/const-default-impl-non-const-specialized-impl.rs:19:1
|
|
||||||
|
|
|
||||||
LL | impl Value for FortyTwo {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue