From 5c25d30f6fe9996f815a96f4b328e62c452cc3e3 Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Thu, 24 Mar 2022 19:24:40 -0500 Subject: [PATCH 1/6] Allow specialized const trait impls. Fixes #95186. Fixes #95187. --- .../src/impl_wf_check/min_specialization.rs | 65 +++++++++++++------ .../const-default-const-specialized.rs | 38 +++++++++++ .../const-default-non-const-specialized.rs | 37 +++++++++++ ...const-default-non-const-specialized.stderr | 37 +++++++++++ .../specialization/default-keyword.rs | 14 ++++ .../issue-95186-specialize-on-tilde-const.rs | 34 ++++++++++ ...87-same-trait-bound-different-constness.rs | 28 ++++++++ .../non-const-default-const-specialized.rs | 34 ++++++++++ ...non-const-default-const-specialized.stderr | 20 ++++++ 9 files changed, 286 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr 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 267077cdab4e6..f65760b9c98ca 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 @@ -80,6 +80,7 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt}; +use tracing::instrument; pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { @@ -103,13 +104,11 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti } /// Check that `impl1` is a sound specialization +#[instrument(level = "debug", skip(tcx))] fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) { if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) { let impl2_def_id = impl2_node.def_id(); - debug!( - "check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)", - impl1_def_id, impl2_def_id, impl2_substs - ); + debug!(?impl2_def_id, ?impl2_substs); let parent_substs = if impl2_node.is_from_trait() { impl2_substs.to_vec() @@ -280,13 +279,13 @@ fn check_static_lifetimes<'tcx>( /// /// Each predicate `P` must be: /// -/// * global (not reference any parameters) -/// * `T: Tr` predicate where `Tr` is an always-applicable trait -/// * on the base `impl impl2` -/// * Currently this check is done using syntactic equality, which is -/// conservative but generally sufficient. -/// * a well-formed predicate of a type argument of the trait being implemented, +/// * Global (not reference any parameters). +/// * A `T: Tr` predicate where `Tr` is an always-applicable trait. +/// * Present on the base impl `impl2`. +/// * This check is done using the `trait_predicates_eq` function below. +/// * A well-formed predicate of a type argument of the trait being implemented, /// including the `Self`-type. +#[instrument(level = "debug", skip(tcx))] fn check_predicates<'tcx>( tcx: TyCtxt<'tcx>, impl1_def_id: LocalDefId, @@ -322,10 +321,7 @@ fn check_predicates<'tcx>( .map(|obligation| obligation.predicate) .collect() }; - debug!( - "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", - impl1_predicates, impl2_predicates, - ); + debug!(?impl1_predicates, ?impl2_predicates); // Since impls of always applicable traits don't get to assume anything, we // can also assume their supertraits apply. @@ -373,25 +369,52 @@ fn check_predicates<'tcx>( ); for (predicate, span) in impl1_predicates { - if !impl2_predicates.contains(&predicate) { + if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(predicate, *pred2)) { check_specialization_on(tcx, predicate, span) } } } +/// Checks whether two predicates are the same for the purposes of specialization. +/// +/// This is slightly more complicated than simple syntactic equivalence, since +/// we want to equate `T: Tr` with `T: ~const Tr` so this can work: +/// +/// #[rustc_specialization_trait] +/// trait Specialize { } +/// +/// impl const Tr for T { } +/// impl Tr for T { } +fn trait_predicates_eq<'tcx>( + predicate1: ty::Predicate<'tcx>, + predicate2: ty::Predicate<'tcx>, +) -> bool { + let predicate_kind_without_constness = |kind: ty::PredicateKind<'tcx>| match kind { + ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity }) => { + ty::PredicateKind::Trait(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::NotConst, + polarity, + }) + } + _ => kind, + }; + + 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); + + pred1_kind_not_const == pred2_kind_not_const +} + +#[instrument(level = "debug", skip(tcx))] fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { - debug!("can_specialize_on(predicate = {:?})", predicate); match predicate.kind().skip_binder() { // Global predicates are either always true or always false, so we // are fine to specialize on. _ if predicate.is_global() => (), // We allow specializing on explicitly marked traits with no associated // items. - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: _, - }) => { + ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => { if !matches!( trait_predicate_kind(tcx, predicate), Some(TraitSpecializationKind::Marker) diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs new file mode 100644 index 0000000000000..1eddfbf50f386 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs @@ -0,0 +1,38 @@ +// Tests that a const default trait impl can be specialized by another const +// trait impl and that the specializing impl will be used during const-eval. + +// run-pass + +#![feature(const_trait_impl)] +#![feature(min_specialization)] + +trait Value { + fn value() -> u32; +} + +const fn get_value() -> u32 { + T::value() +} + +impl const Value for T { + default fn value() -> u32 { + 0 + } +} + +struct FortyTwo; + +impl const Value for FortyTwo { + fn value() -> u32 { + 42 + } +} + +const ZERO: u32 = get_value::<()>(); + +const FORTY_TWO: u32 = get_value::(); + +fn main() { + assert_eq!(ZERO, 0); + assert_eq!(FORTY_TWO, 42); +} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs new file mode 100644 index 0000000000000..31de6fadeb7a2 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs @@ -0,0 +1,37 @@ +// Tests that a const default trait impl can be specialized by a non-const trait +// impl, but that the specializing impl cannot be used in a const context. + +#![feature(const_trait_impl)] +#![feature(min_specialization)] + +trait Value { + fn value() -> u32; +} + +const fn get_value() -> u32 { + T::value() + //~^ ERROR any use of this value will cause an error [const_err] + //~| WARNING this was previously accepted +} + +impl const Value for T { + default fn value() -> u32 { + 0 + } +} + +struct FortyTwo; + +impl Value for FortyTwo { + fn value() -> u32 { + println!("You can't do that (constly)"); + 42 + } +} + +const ZERO: u32 = get_value::<()>(); + +const FORTY_TWO: u32 = + get_value::(); // This is the line that causes the error, but it gets reported above + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr new file mode 100644 index 0000000000000..7dfd489ea65c6 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr @@ -0,0 +1,37 @@ +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 `::value` + | inside `get_value::` 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 + +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 `::value` + | inside `get_value::` 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 + diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs new file mode 100644 index 0000000000000..c03b0a0d19ca5 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs @@ -0,0 +1,14 @@ +// check-pass + +#![feature(const_trait_impl)] +#![feature(min_specialization)] + +trait Foo { + fn foo(); +} + +impl const Foo for u32 { + default fn foo() {} +} + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs new file mode 100644 index 0000000000000..1f7f47879d78b --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs @@ -0,0 +1,34 @@ +// Tests that `~const` trait bounds can be used to specialize const trait impls. + +// check-pass + +#![feature(const_trait_impl)] +#![feature(rustc_attrs)] +#![feature(min_specialization)] + +#[rustc_specialization_trait] +trait Specialize {} + +trait Foo {} + +impl const Foo for T {} + +impl const Foo for T +where + T: ~const Specialize, +{} + +trait Bar {} + +impl const Bar for T +where + T: ~const Foo, +{} + +impl const Bar for T +where + T: ~const Foo, + T: ~const Specialize, +{} + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs new file mode 100644 index 0000000000000..f6daba5595a8d --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs @@ -0,0 +1,28 @@ +// Tests that `T: ~const Foo` and `T: Foo` are treated as equivalent for the +// purposes of min_specialization. + +// check-pass + +#![feature(rustc_attrs)] +#![feature(min_specialization)] +#![feature(const_trait_impl)] + +#[rustc_specialization_trait] +trait Specialize {} + +trait Foo {} + +trait Bar {} + +impl const Bar for T +where + T: ~const Foo, +{} + +impl Bar for T +where + T: Foo, + T: Specialize, +{} + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs new file mode 100644 index 0000000000000..cf6c292e8a465 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs @@ -0,0 +1,34 @@ +// 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. + +#![feature(const_trait_impl)] +#![feature(min_specialization)] + +trait Value { + fn value() -> u32; +} + +const fn get_value() -> u32 { + T::value() +} + +impl Value for T { + default fn value() -> u32 { + println!("You can't do that (constly)"); + 0 + } +} + +struct FortyTwo; + +impl const Value for FortyTwo { + fn value() -> u32 { + 42 + } +} + +const ZERO: u32 = get_value::<()>(); //~ ERROR the trait bound `(): ~const Value` is not satisfied + +const FORTY_TWO: u32 = get_value::(); + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr new file mode 100644 index 0000000000000..1065009c8910e --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr @@ -0,0 +1,20 @@ +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() -> u32 { + | ^^^^^^^^^^^^ required by this bound in `get_value` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From ce03d259da4a9e47111465353363597868aa2266 Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Tue, 13 Sep 2022 22:18:14 -0500 Subject: [PATCH 2/6] Disallow specializing on const impls with non-const impls. --- .../src/impl_wf_check/min_specialization.rs | 35 ++++++++++++++++-- .../const-default-non-const-specialized.rs | 17 ++------- ...const-default-non-const-specialized.stderr | 37 ++----------------- ...87-same-trait-bound-different-constness.rs | 10 ++--- .../non-const-default-const-specialized.rs | 12 ++++-- ...non-const-default-const-specialized.stderr | 20 ---------- 6 files changed, 51 insertions(+), 80 deletions(-) delete mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr 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 f65760b9c98ca..9c7150b79240a 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 @@ -69,6 +69,7 @@ use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; 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); + check_constness(tcx, impl1_def_id, impl2_node, span); check_static_lifetimes(tcx, &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 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 /// substitutions `(S1, S2)` that equate their trait references. The returned /// 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. /// -/// Each predicate `P` must be: +/// Each predicate `P` must be one of: /// /// * Global (not reference any parameters). /// * 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 /// we want to equate `T: Tr` with `T: ~const Tr` so this can work: /// +/// ```ignore (illustrative) /// #[rustc_specialization_trait] /// trait Specialize { } /// -/// impl const Tr for T { } -/// impl Tr for T { } +/// impl Tr for T { } +/// impl const Tr for T { } +/// ``` fn trait_predicates_eq<'tcx>( predicate1: ty::Predicate<'tcx>, predicate2: ty::Predicate<'tcx>, @@ -400,6 +425,8 @@ fn trait_predicates_eq<'tcx>( _ => 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 pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness); diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs index 31de6fadeb7a2..a25329ba388d2 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs @@ -1,5 +1,5 @@ -// Tests that a const default trait impl can be specialized by a non-const trait -// impl, but that the specializing impl cannot be used in a const context. +// Tests that a const default trait impl cannot be specialized by a non-const +// trait impl. #![feature(const_trait_impl)] #![feature(min_specialization)] @@ -8,12 +8,6 @@ trait Value { fn value() -> u32; } -const fn get_value() -> u32 { - T::value() - //~^ ERROR any use of this value will cause an error [const_err] - //~| WARNING this was previously accepted -} - impl const Value for T { default fn value() -> u32 { 0 @@ -22,16 +16,11 @@ impl const Value for T { struct FortyTwo; -impl Value for FortyTwo { +impl Value for FortyTwo { //~ ERROR cannot specialize on const impl with non-const impl fn value() -> u32 { println!("You can't do that (constly)"); 42 } } -const ZERO: u32 = get_value::<()>(); - -const FORTY_TWO: u32 = - get_value::(); // This is the line that causes the error, but it gets reported above - fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr index 7dfd489ea65c6..b0b76e7eca8ac 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr @@ -1,37 +1,8 @@ -error: any use of this value will cause an error - --> $DIR/const-default-non-const-specialized.rs:12:5 +error: cannot specialize on const impl with non-const impl + --> $DIR/const-default-non-const-specialized.rs:19:1 | -LL | T::value() - | ^^^^^^^^^^ - | | - | calling non-const function `::value` - | inside `get_value::` 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 +LL | impl Value for FortyTwo { + | ^^^^^^^^^^^^^^^^^^^^^^^ 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 `::value` - | inside `get_value::` 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 - diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs index f6daba5595a8d..da6df064d4fd1 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs @@ -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. // check-pass @@ -14,14 +14,14 @@ trait Foo {} trait Bar {} -impl const Bar for T +impl Bar for T where - T: ~const Foo, + T: Foo, {} -impl Bar for T +impl const Bar for T where - T: Foo, + T: ~const Foo, T: Specialize, {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs index cf6c292e8a465..84614f5459d63 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs @@ -1,6 +1,8 @@ // 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. +// run-pass + #![feature(const_trait_impl)] #![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 - -const FORTY_TWO: u32 = get_value::(); +fn main() { + let zero = get_value::<()>(); + assert_eq!(zero, 0); -fn main() {} + const FORTY_TWO: u32 = get_value::(); + assert_eq!(FORTY_TWO, 42); +} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr deleted file mode 100644 index 1065009c8910e..0000000000000 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr +++ /dev/null @@ -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() -> u32 { - | ^^^^^^^^^^^^ required by this bound in `get_value` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0277`. From d492b9b000553f1407d9b75e15412d9df50314b2 Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Sat, 5 Nov 2022 23:12:57 -0500 Subject: [PATCH 3/6] Add #[const_trait] where needed in tests. --- .../specialization/const-default-const-specialized.rs | 1 + .../specialization/const-default-non-const-specialized.rs | 1 + .../specialization/const-default-non-const-specialized.stderr | 2 +- .../specialization/default-keyword.rs | 1 + .../specialization/issue-95186-specialize-on-tilde-const.rs | 3 +++ .../issue-95187-same-trait-bound-different-constness.rs | 2 ++ .../specialization/non-const-default-const-specialized.rs | 1 + 7 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs index 1eddfbf50f386..9ddea427cfd80 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs @@ -6,6 +6,7 @@ #![feature(const_trait_impl)] #![feature(min_specialization)] +#[const_trait] trait Value { fn value() -> u32; } diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs index a25329ba388d2..79dd4950af32a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs @@ -4,6 +4,7 @@ #![feature(const_trait_impl)] #![feature(min_specialization)] +#[const_trait] trait Value { fn value() -> u32; } diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr index b0b76e7eca8ac..5232a8609cd80 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr @@ -1,5 +1,5 @@ error: cannot specialize on const impl with non-const impl - --> $DIR/const-default-non-const-specialized.rs:19:1 + --> $DIR/const-default-non-const-specialized.rs:20:1 | LL | impl Value for FortyTwo { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs index c03b0a0d19ca5..2aac0a2b4d111 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs @@ -3,6 +3,7 @@ #![feature(const_trait_impl)] #![feature(min_specialization)] +#[const_trait] trait Foo { fn foo(); } diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs index 1f7f47879d78b..9c2c2cf1610a2 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs @@ -6,9 +6,11 @@ #![feature(rustc_attrs)] #![feature(min_specialization)] +#[const_trait] #[rustc_specialization_trait] trait Specialize {} +#[const_trait] trait Foo {} impl const Foo for T {} @@ -18,6 +20,7 @@ where T: ~const Specialize, {} +#[const_trait] trait Bar {} impl const Bar for T diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs index da6df064d4fd1..3370aebf0634a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs @@ -10,8 +10,10 @@ #[rustc_specialization_trait] trait Specialize {} +#[const_trait] trait Foo {} +#[const_trait] trait Bar {} impl Bar for T diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs index 84614f5459d63..35aa52fbd4ed2 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs @@ -6,6 +6,7 @@ #![feature(const_trait_impl)] #![feature(min_specialization)] +#[const_trait] trait Value { fn value() -> u32; } From c0ae62ee955712c96dec17170d4d63fd2b34f504 Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Sun, 6 Nov 2022 01:07:06 -0500 Subject: [PATCH 4/6] Require `~const` qualifier on trait bounds in specializing impls if present in base impl. --- .../src/impl_wf_check/min_specialization.rs | 59 ++++++++++++++----- ...fault-bound-non-const-specialized-bound.rs | 46 +++++++++++++++ ...t-bound-non-const-specialized-bound.stderr | 18 ++++++ ...efault-impl-non-const-specialized-impl.rs} | 3 +- ...lt-impl-non-const-specialized-impl.stderr} | 2 +- ...87-same-trait-bound-different-constness.rs | 19 +++++- .../specializing-constness.rs | 4 +- .../specializing-constness.stderr | 10 +++- 8 files changed, 137 insertions(+), 24 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr rename src/test/ui/rfc-2632-const-trait-impl/specialization/{const-default-non-const-specialized.rs => const-default-impl-non-const-specialized-impl.rs} (81%) rename src/test/ui/rfc-2632-const-trait-impl/specialization/{const-default-non-const-specialized.stderr => const-default-impl-non-const-specialized-impl.stderr} (71%) 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 9c7150b79240a..f3cb558ef7093 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 @@ -391,7 +391,7 @@ fn check_predicates<'tcx>( ); for (predicate, span) in impl1_predicates { - if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(predicate, *pred2)) { + if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) { check_specialization_on(tcx, predicate, span) } } @@ -400,8 +400,8 @@ fn check_predicates<'tcx>( /// 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 -/// we want to equate `T: Tr` with `T: ~const Tr` so this can work: +/// This basically just checks syntactic equivalence, but is a little more +/// forgiving since we want to equate `T: Tr` with `T: ~const Tr` so this can work: /// /// ```ignore (illustrative) /// #[rustc_specialization_trait] @@ -410,27 +410,54 @@ fn check_predicates<'tcx>( /// impl Tr for T { } /// impl const Tr for T { } /// ``` +/// +/// However, we *don't* want to allow the reverse, i.e., when the bound on the +/// specializing impl is not as const as the bound on the base impl: +/// +/// ```ignore (illustrative) +/// impl const Tr for T { } +/// impl const Tr for T { } // should be T: ~const Bound +/// ``` +/// +/// So we make that check in this function and try to raise a helpful error message. fn trait_predicates_eq<'tcx>( + tcx: TyCtxt<'tcx>, predicate1: ty::Predicate<'tcx>, predicate2: ty::Predicate<'tcx>, + span: Span, ) -> bool { - let predicate_kind_without_constness = |kind: ty::PredicateKind<'tcx>| match kind { - ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity }) => { - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity, - }) + let pred1_kind = predicate1.kind().no_bound_vars(); + let pred2_kind = predicate2.kind().no_bound_vars(); + let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) { + (Some(ty::PredicateKind::Trait(pred1)), Some(ty::PredicateKind::Trait(pred2))) => { + (pred1, pred2) } - _ => kind, + // Just use plain syntactic equivalence if either of the predicates aren't + // trait predicates or have bound vars. + _ => return pred1_kind == pred2_kind, + }; + + let predicates_equal_modulo_constness = { + let pred1_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 }; + let pred2_unconsted = + ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 }; + pred1_unconsted == pred2_unconsted }; - // 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 pred2_kind_not_const = predicate2.kind().map_bound(predicate_kind_without_constness); + if !predicates_equal_modulo_constness { + return false; + } + + // Check that the predicate on the specializing impl is at least as const as + // the one on the base. + if trait_pred2.constness == ty::BoundConstness::ConstIfConst + && trait_pred1.constness == ty::BoundConstness::NotConst + { + tcx.sess.struct_span_err(span, "missing `~const` qualifier").emit(); + } - pred1_kind_not_const == pred2_kind_not_const + true } #[instrument(level = "debug", skip(tcx))] diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs new file mode 100644 index 0000000000000..3ac909924864d --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs @@ -0,0 +1,46 @@ +// Tests that trait bounds on specializing trait impls must be `~const` if the +// same bound is present on the default impl and is `~const` there. + +#![feature(const_trait_impl)] +#![feature(rustc_attrs)] +#![feature(min_specialization)] + +#[rustc_specialization_trait] +trait Specialize {} + +#[const_trait] +trait Foo {} + +#[const_trait] +trait Bar {} + +// bgr360: I was only able to exercise the code path that raises the +// "missing ~const qualifier" error by making this base impl non-const, even +// though that doesn't really make sense to do. As seen below, if the base impl +// is made const, rustc fails earlier with an overlapping impl failure. +impl Bar for T +where + T: ~const Foo, +{} + +impl Bar for T +where + T: Foo, //~ ERROR missing `~const` qualifier + T: Specialize, +{} + +#[const_trait] +trait Baz {} + +impl const Baz for T +where + T: ~const Foo, +{} + +impl const Baz for T //~ ERROR conflicting implementations of trait `Baz` +where + T: Foo, + T: Specialize, +{} + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr new file mode 100644 index 0000000000000..583c4cec77fb6 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr @@ -0,0 +1,18 @@ +error: missing `~const` qualifier + --> $DIR/const-default-bound-non-const-specialized-bound.rs:28:8 + | +LL | T: Foo, + | ^^^ + +error[E0119]: conflicting implementations of trait `Baz` + --> $DIR/const-default-bound-non-const-specialized-bound.rs:40:1 + | +LL | impl const Baz for T + | ----------------------- first implementation here +... +LL | impl const Baz for T + | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs similarity index 81% rename from src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs rename to src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs index 79dd4950af32a..a3bb9b3f93eda 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs @@ -1,5 +1,4 @@ -// Tests that a const default trait impl cannot be specialized by a non-const -// trait impl. +// Tests that specializing trait impls must be at least as const as the default impl. #![feature(const_trait_impl)] #![feature(min_specialization)] diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr similarity index 71% rename from src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr rename to src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr index 5232a8609cd80..24766804708a3 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-non-const-specialized.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr @@ -1,5 +1,5 @@ error: cannot specialize on const impl with non-const impl - --> $DIR/const-default-non-const-specialized.rs:20:1 + --> $DIR/const-default-impl-non-const-specialized-impl.rs:19:1 | LL | impl Value for FortyTwo { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs index 3370aebf0634a..1e6b1c6513b39 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs @@ -1,5 +1,6 @@ -// Tests that `T: Foo` and `T: ~const Foo` are treated as equivalent for the -// purposes of min_specialization. +// Tests that `T: ~const Foo` in a specializing impl is treated as equivalent to +// `T: Foo` in the default impl for the purposes of specialization (i.e., it +// does not think that the user is attempting to specialize on trait `Foo`). // check-pass @@ -27,4 +28,18 @@ where T: Specialize, {} +#[const_trait] +trait Baz {} + +impl const Baz for T +where + T: Foo, +{} + +impl const Baz for T +where + T: ~const Foo, + T: Specialize, +{} + fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs index ff0cd489d4744..9ab170f092006 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.rs @@ -17,7 +17,9 @@ impl const A for T { } } -impl A for T { //~ ERROR: cannot specialize +impl A for T { +//~^ ERROR: cannot specialize +//~| ERROR: missing `~const` qualifier fn a() -> u32 { 3 } diff --git a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr index 3296c109c4e73..281ba82d64429 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr @@ -1,8 +1,14 @@ -error: cannot specialize on trait `Default` +error: cannot specialize on const impl with non-const impl + --> $DIR/specializing-constness.rs:20:1 + | +LL | impl A for T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing `~const` qualifier --> $DIR/specializing-constness.rs:20:9 | LL | impl A for T { | ^^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors From fe53cacff9f1e2aaeaeea4116d99cfa24d1f460f Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Thu, 10 Nov 2022 12:56:09 -0600 Subject: [PATCH 5/6] Apply PR feedback. --- .../src/impl_wf_check/min_specialization.rs | 19 +++++++++---------- ...t-bound-non-const-specialized-bound.stderr | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) 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 f3cb558ef7093..55cca0cd2d7b5 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 @@ -426,15 +426,13 @@ fn trait_predicates_eq<'tcx>( predicate2: ty::Predicate<'tcx>, span: Span, ) -> bool { - let pred1_kind = predicate1.kind().no_bound_vars(); - let pred2_kind = predicate2.kind().no_bound_vars(); + let pred1_kind = predicate1.kind().skip_binder(); + let pred2_kind = predicate2.kind().skip_binder(); let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) { - (Some(ty::PredicateKind::Trait(pred1)), Some(ty::PredicateKind::Trait(pred2))) => { - (pred1, pred2) - } + (ty::PredicateKind::Trait(pred1), ty::PredicateKind::Trait(pred2)) => (pred1, pred2), // Just use plain syntactic equivalence if either of the predicates aren't // trait predicates or have bound vars. - _ => return pred1_kind == pred2_kind, + _ => return predicate1 == predicate2, }; let predicates_equal_modulo_constness = { @@ -451,10 +449,11 @@ fn trait_predicates_eq<'tcx>( // Check that the predicate on the specializing impl is at least as const as // the one on the base. - if trait_pred2.constness == ty::BoundConstness::ConstIfConst - && trait_pred1.constness == ty::BoundConstness::NotConst - { - tcx.sess.struct_span_err(span, "missing `~const` qualifier").emit(); + match (trait_pred2.constness, trait_pred1.constness) { + (ty::BoundConstness::ConstIfConst, ty::BoundConstness::NotConst) => { + tcx.sess.struct_span_err(span, "missing `~const` qualifier for specialization").emit(); + } + _ => {} } true diff --git a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr index 583c4cec77fb6..4aea1979421c3 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr @@ -1,4 +1,4 @@ -error: missing `~const` qualifier +error: missing `~const` qualifier for specialization --> $DIR/const-default-bound-non-const-specialized-bound.rs:28:8 | LL | T: Foo, From 94f67e667be3efd1845bb95fcd25fcce11cf983c Mon Sep 17 00:00:00 2001 From: Ben Reeves Date: Thu, 10 Nov 2022 22:14:08 -0600 Subject: [PATCH 6/6] Oops, bless this test. --- .../ui/rfc-2632-const-trait-impl/specializing-constness.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr index 281ba82d64429..843fc6ce84d45 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/specializing-constness.stderr @@ -4,7 +4,7 @@ error: cannot specialize on const impl with non-const impl LL | impl A for T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing `~const` qualifier +error: missing `~const` qualifier for specialization --> $DIR/specializing-constness.rs:20:9 | LL | impl A for T {