From e91fc1bc0c05da68d218a01d550c6d12297f5703 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 21 Nov 2024 20:22:08 +0000 Subject: [PATCH 1/2] Reimplement specialization for const traits --- .../src/traits/specialize/mod.rs | 130 +++++++++++++++--- ...t-bound-non-const-specialized-bound.stderr | 26 +++- .../const-traits/specializing-constness.rs | 3 +- .../specializing-constness.stderr | 8 +- 4 files changed, 142 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index a9cd705465e3a..91a0599a3be83 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -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 _; @@ -230,15 +231,18 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool /// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies /// to. #[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 +250,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 +265,117 @@ 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(), - ) - .is_ok() + 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. + if tcx.is_conditionally_const(parent_impl_def_id) { + 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), + ) + })); + + 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`. diff --git a/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr b/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr index bffc60c65fcea..9166b8ca5d22b 100644 --- a/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr +++ b/tests/ui/traits/const-traits/specialization/const-default-bound-non-const-specialized-bound.stderr @@ -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 const Bar for T +LL | | where +LL | | T: ~const Foo, + | |__________________- first implementation here +... LL | / impl Bar for T LL | | where LL | | T: Foo, //FIXME ~ ERROR missing `~const` qualifier LL | | T: Specialize, - | |__________________^ + | |__________________^ conflicting implementation + +error[E0119]: conflicting implementations of trait `Baz` + --> $DIR/const-default-bound-non-const-specialized-bound.rs:48:1 + | +LL | / impl const Baz for T +LL | | where +LL | | T: ~const Foo, + | |__________________- first implementation here +... +LL | / impl 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 1 previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/const-traits/specializing-constness.rs b/tests/ui/traits/const-traits/specializing-constness.rs index 632121924a63b..94b6da7124d29 100644 --- a/tests/ui/traits/const-traits/specializing-constness.rs +++ b/tests/ui/traits/const-traits/specializing-constness.rs @@ -21,8 +21,7 @@ impl const A for T { } impl A for T { -//~^ ERROR: cannot specialize -//FIXME(const_trait_impl) ~| ERROR: missing `~const` qualifier + //~^ ERROR conflicting implementations of trait `A` fn a() -> u32 { 3 } diff --git a/tests/ui/traits/const-traits/specializing-constness.stderr b/tests/ui/traits/const-traits/specializing-constness.stderr index 21e21c2cb71aa..2ca70b53e4e21 100644 --- a/tests/ui/traits/const-traits/specializing-constness.stderr +++ b/tests/ui/traits/const-traits/specializing-constness.stderr @@ -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 const A for T { + | ---------------------------------- first implementation here +... LL | impl A for T { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0119`. From 9bda88bb58c1c6fa175166a7d5272f483eac38c8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Nov 2024 01:00:11 +0000 Subject: [PATCH 2/2] 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 | 22 ++++++++--- ...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, 133 insertions(+), 50 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 cb658111392c3..070d63b48b7bd 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 51115b11e86b8..4142dcff226bf 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -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 { 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 91a0599a3be83..1430cfae51f73 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -225,11 +225,17 @@ 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<'_>, @@ -339,8 +345,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 -