From ebfd56f6754c967eb7b18afe0e9a0996eb0c33c0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Nov 2024 01:00:11 +0000 Subject: [PATCH] Fix const specialization --- compiler/rustc_hir_analysis/messages.ftl | 2 - compiler/rustc_hir_analysis/src/errors.rs | 7 ---- .../src/impl_wf_check/min_specialization.rs | 26 ------------- .../src/traits/specialize/mod.rs | 8 +++- ...verlap-const-with-nonconst.min_spec.stderr | 14 +++++++ .../overlap-const-with-nonconst.rs | 38 +++++++++++++++++++ .../overlap-const-with-nonconst.spec.stderr | 24 ++++++++++++ ...non-const-specialized-impl.min_spec.stderr | 12 ++++++ ...default-impl-non-const-specialized-impl.rs | 8 +++- ...mpl-non-const-specialized-impl.spec.stderr | 22 +++++++++++ ...ult-impl-non-const-specialized-impl.stderr | 8 ---- 11 files changed, 123 insertions(+), 46 deletions(-) create mode 100644 tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr create mode 100644 tests/ui/traits/const-traits/overlap-const-with-nonconst.rs create mode 100644 tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr create mode 100644 tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr create mode 100644 tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr delete mode 100644 tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 64a30e633cf9d..aa48eccec9405 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -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 diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 07d3273b09c77..9493b6126b491 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1057,13 +1057,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 { diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 246643d807470..ee55e1bc21a60 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -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`. diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index c9b402d37cafc..a3713174b3bdb 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -339,8 +339,14 @@ pub(super) fn specializes( 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(specializing_impl_def_id) { + return false; + } + let const_conditions = ocx.normalize( cause, param_env, diff --git a/tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr b/tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr new file mode 100644 index 0000000000000..bd822970ad1ef --- /dev/null +++ b/tests/ui/traits/const-traits/overlap-const-with-nonconst.min_spec.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `Foo` for type `(_,)` + --> $DIR/overlap-const-with-nonconst.rs:23:1 + | +LL | / impl const Foo for T +LL | | where +LL | | T: ~const Bar, + | |__________________- first implementation here +... +LL | impl Foo for (T,) { + | ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(_,)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/const-traits/overlap-const-with-nonconst.rs b/tests/ui/traits/const-traits/overlap-const-with-nonconst.rs new file mode 100644 index 0000000000000..eb66d03faa638 --- /dev/null +++ b/tests/ui/traits/const-traits/overlap-const-with-nonconst.rs @@ -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 const Bar for T {} + +#[const_trait] +trait Foo { + fn method(&self); +} +impl const Foo for T +where + T: ~const Bar, +{ + default fn method(&self) {} +} +// specializing impl: +impl Foo for (T,) { +//~^ ERROR conflicting implementations + fn method(&self) { + println!("hi"); + } +} + +const fn dispatch(t: T) { + t.method(); +} + +fn main() { + const { + dispatch(((),)); + } +} diff --git a/tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr b/tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr new file mode 100644 index 0000000000000..cbdcb45f6beba --- /dev/null +++ b/tests/ui/traits/const-traits/overlap-const-with-nonconst.spec.stderr @@ -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 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 const Foo for T +LL | | where +LL | | T: ~const Bar, + | |__________________- first implementation here +... +LL | impl 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`. diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr new file mode 100644 index 0000000000000..38fc5ddfbef52 --- /dev/null +++ b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.min_spec.stderr @@ -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 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`. diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs index a3bb9b3f93eda..acf0a967a884d 100644 --- a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs +++ b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.rs @@ -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 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 diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr new file mode 100644 index 0000000000000..b59c42f518936 --- /dev/null +++ b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.spec.stderr @@ -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 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 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`. diff --git a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr b/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr deleted file mode 100644 index e356621ba47ed..0000000000000 --- a/tests/ui/traits/const-traits/specialization/const-default-impl-non-const-specialized-impl.stderr +++ /dev/null @@ -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 -