Rollup merge of #133325 - compiler-errors:const-spec, r=lcnr,fee1-dead
Reimplement `~const` trait specialization Reimplement const specialization. We need this for `PartialEq` constification :) r? lcnr
This commit is contained in:
commit
8aa5853b58
14 changed files with 274 additions and 74 deletions
|
@ -113,8 +113,6 @@ hir_analysis_const_param_ty_impl_on_unsized =
|
|||
the trait `ConstParamTy` may not be implemented for this type
|
||||
.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 =
|
||||
the trait `Copy` cannot be implemented for this type
|
||||
.label = type is not a structure or enumeration
|
||||
|
|
|
@ -1079,13 +1079,6 @@ pub(crate) struct EmptySpecialization {
|
|||
pub base_impl_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_const_specialize)]
|
||||
pub(crate) struct ConstSpecialize {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_static_specialize)]
|
||||
pub(crate) struct StaticSpecialize {
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
//! on traits with methods can.
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
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)
|
||||
};
|
||||
|
||||
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_duplicate_params(tcx, impl1_args, parent_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(())
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// generic parameters `(S1, S2)` that equate their trait references.
|
||||
/// The returned types are expressed in terms of the generics of `impl1`.
|
||||
|
|
|
@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxIndexSet;
|
|||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Diag, EmissionGuarantee};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::query::LocalCrate;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
|
@ -224,21 +225,30 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool
|
|||
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;
|
||||
/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
|
||||
/// to.
|
||||
/// For every type that could apply to `specializing_impl_def_id`, we prove that
|
||||
/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and
|
||||
/// 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")]
|
||||
pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool {
|
||||
pub(super) fn specializes(
|
||||
tcx: TyCtxt<'_>,
|
||||
(specializing_impl_def_id, parent_impl_def_id): (DefId, DefId),
|
||||
) -> bool {
|
||||
// We check that the specializing impl comes from a crate that has specialization enabled,
|
||||
// or if the specializing impl is marked with `allow_internal_unstable`.
|
||||
//
|
||||
// We don't really care if the specialized impl (the parent) is in a crate that has
|
||||
// specialization enabled, since it's not being specialized, and it's already been checked
|
||||
// for coherence.
|
||||
if !tcx.specialization_enabled_in(impl1_def_id.krate) {
|
||||
let span = tcx.def_span(impl1_def_id);
|
||||
if !tcx.specialization_enabled_in(specializing_impl_def_id.krate) {
|
||||
let span = tcx.def_span(specializing_impl_def_id);
|
||||
if !span.allows_unstable(sym::specialization)
|
||||
&& !span.allows_unstable(sym::min_specialization)
|
||||
{
|
||||
|
@ -246,7 +256,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
|
|||
}
|
||||
}
|
||||
|
||||
let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap();
|
||||
let specializing_impl_trait_header = tcx.impl_trait_header(specializing_impl_def_id).unwrap();
|
||||
|
||||
// We determine whether there's a subset relationship by:
|
||||
//
|
||||
|
@ -261,27 +271,123 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
|
|||
// See RFC 1210 for more details and justification.
|
||||
|
||||
// Currently we do not allow e.g., a negative impl to specialize a positive one
|
||||
if impl1_trait_header.polarity != tcx.impl_polarity(impl2_def_id) {
|
||||
if specializing_impl_trait_header.polarity != tcx.impl_polarity(parent_impl_def_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// create a parameter environment corresponding to an identity instantiation of impl1,
|
||||
// i.e. the most generic instantiation of impl1.
|
||||
let param_env = tcx.param_env(impl1_def_id);
|
||||
// create a parameter environment corresponding to an identity instantiation of the specializing impl,
|
||||
// i.e. the most generic instantiation of the specializing impl.
|
||||
let param_env = tcx.param_env(specializing_impl_def_id);
|
||||
|
||||
// Create an infcx, taking the predicates of impl1 as assumptions:
|
||||
// Create an infcx, taking the predicates of the specializing impl as assumptions:
|
||||
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
|
||||
// Attempt to prove that impl2 applies, given all of the above.
|
||||
fulfill_implication(
|
||||
&infcx,
|
||||
let specializing_impl_trait_ref =
|
||||
specializing_impl_trait_header.trait_ref.instantiate_identity();
|
||||
let cause = &ObligationCause::dummy();
|
||||
debug!(
|
||||
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
|
||||
param_env, specializing_impl_trait_ref, parent_impl_def_id
|
||||
);
|
||||
|
||||
// Attempt to prove that the parent impl applies, given all of the above.
|
||||
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let specializing_impl_trait_ref = ocx.normalize(cause, param_env, specializing_impl_trait_ref);
|
||||
|
||||
if !ocx.select_all_or_error().is_empty() {
|
||||
infcx.dcx().span_delayed_bug(
|
||||
infcx.tcx.def_span(specializing_impl_def_id),
|
||||
format!("failed to fully normalize {specializing_impl_trait_ref}"),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let parent_args = infcx.fresh_args_for_item(DUMMY_SP, parent_impl_def_id);
|
||||
let parent_impl_trait_ref = ocx.normalize(
|
||||
cause,
|
||||
param_env,
|
||||
impl1_trait_header.trait_ref.instantiate_identity(),
|
||||
impl1_def_id,
|
||||
impl2_def_id,
|
||||
&ObligationCause::dummy(),
|
||||
infcx
|
||||
.tcx
|
||||
.impl_trait_ref(parent_impl_def_id)
|
||||
.expect("expected source impl to be a trait impl")
|
||||
.instantiate(infcx.tcx, parent_args),
|
||||
);
|
||||
|
||||
// do the impls unify? If not, no specialization.
|
||||
let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Now check that the source trait ref satisfies all the where clauses of the target impl.
|
||||
// This is not just for correctness; we also need this to constrain any params that may
|
||||
// only be referenced via projection predicates.
|
||||
let predicates = ocx.normalize(
|
||||
cause,
|
||||
param_env,
|
||||
infcx.tcx.predicates_of(parent_impl_def_id).instantiate(infcx.tcx, parent_args),
|
||||
);
|
||||
let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates);
|
||||
ocx.register_obligations(obligations);
|
||||
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
// no dice!
|
||||
debug!(
|
||||
"fulfill_implication: for impls on {:?} and {:?}, \
|
||||
could not fulfill: {:?} given {:?}",
|
||||
specializing_impl_trait_ref,
|
||||
parent_impl_trait_ref,
|
||||
errors,
|
||||
param_env.caller_bounds()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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(specializing_impl_def_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let const_conditions = ocx.normalize(
|
||||
cause,
|
||||
param_env,
|
||||
infcx.tcx.const_conditions(parent_impl_def_id).instantiate(infcx.tcx, parent_args),
|
||||
);
|
||||
ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, _)| {
|
||||
Obligation::new(
|
||||
infcx.tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
trait_ref.to_host_effect_clause(infcx.tcx, ty::BoundConstness::Maybe),
|
||||
)
|
||||
.is_ok()
|
||||
}));
|
||||
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
// no dice!
|
||||
debug!(
|
||||
"fulfill_implication: for impls on {:?} and {:?}, \
|
||||
could not fulfill: {:?} given {:?}",
|
||||
specializing_impl_trait_ref,
|
||||
parent_impl_trait_ref,
|
||||
errors,
|
||||
param_env.caller_bounds()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"fulfill_implication: an impl for {:?} specializes {:?}",
|
||||
specializing_impl_trait_ref, parent_impl_trait_ref
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Query provider for `specialization_graph_of`.
|
||||
|
|
|
@ -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`.
|
|
@ -1,11 +1,31 @@
|
|||
error: cannot specialize on const impl with non-const impl
|
||||
error[E0119]: conflicting implementations of trait `Bar`
|
||||
--> $DIR/const-default-bound-non-const-specialized-bound.rs:28:1
|
||||
|
|
||||
LL | / impl<T> const Bar for T
|
||||
LL | | where
|
||||
LL | | T: ~const Foo,
|
||||
| |__________________- first implementation here
|
||||
...
|
||||
LL | / impl<T> Bar for T
|
||||
LL | | where
|
||||
LL | | T: Foo, //FIXME ~ ERROR missing `~const` qualifier
|
||||
LL | | T: Specialize,
|
||||
| |__________________^
|
||||
| |__________________^ conflicting implementation
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error[E0119]: conflicting implementations of trait `Baz`
|
||||
--> $DIR/const-default-bound-non-const-specialized-bound.rs:48:1
|
||||
|
|
||||
LL | / impl<T> const Baz for T
|
||||
LL | | where
|
||||
LL | | T: ~const Foo,
|
||||
| |__________________- first implementation here
|
||||
...
|
||||
LL | / impl<T> const Baz for T //FIXME ~ ERROR conflicting implementations of trait `Baz`
|
||||
LL | | where
|
||||
LL | | T: Foo,
|
||||
LL | | T: Specialize,
|
||||
| |__________________^ conflicting implementation
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
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.
|
||||
//@ revisions: spec min_spec
|
||||
|
||||
#![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]
|
||||
trait Value {
|
||||
|
@ -16,7 +19,8 @@ impl<T> const Value for T {
|
|||
|
||||
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 {
|
||||
println!("You can't do that (constly)");
|
||||
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
|
||||
|
|
@ -21,8 +21,7 @@ impl<T: ~const Spec> const A for T {
|
|||
}
|
||||
|
||||
impl<T: Spec + Sup> A for T {
|
||||
//~^ ERROR: cannot specialize
|
||||
//FIXME(const_trait_impl) ~| ERROR: missing `~const` qualifier
|
||||
//~^ ERROR conflicting implementations of trait `A`
|
||||
fn a() -> u32 {
|
||||
3
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
error: cannot specialize on const impl with non-const impl
|
||||
error[E0119]: conflicting implementations of trait `A`
|
||||
--> $DIR/specializing-constness.rs:23:1
|
||||
|
|
||||
LL | impl<T: ~const Spec> const A for T {
|
||||
| ---------------------------------- first implementation here
|
||||
...
|
||||
LL | impl<T: Spec + Sup> A for T {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0119`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue