From 899d9e67692012b665755824b027adeb014497be Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 31 Dec 2024 03:08:55 +0000 Subject: [PATCH 1/2] Suppress host effect predicates if underlying trait doesn't hold --- .../traits/fulfillment_errors.rs | 53 +++++++++++++------ .../double-error-for-unimplemented-trait.rs | 22 ++++++++ ...ouble-error-for-unimplemented-trait.stderr | 41 ++++++++++++++ 3 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs create mode 100644 tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 885b606326c54..163c453bdbe7f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -538,23 +538,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { - // FIXME(const_trait_impl): We should recompute the predicate with `~const` - // if it's `const`, and if it holds, explain that this bound only - // *conditionally* holds. If that fails, we should also do selection - // to drill this down to an impl or built-in source, so we can - // point at it and explain that while the trait *is* implemented, - // that implementation is not const. - let err_msg = self.get_standard_error_message( - bound_predicate.rebind(ty::TraitPredicate { - trait_ref: predicate.trait_ref, - polarity: ty::PredicatePolarity::Positive, - }), - None, - Some(predicate.constness), - None, - String::new(), - ); - struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg) + self.report_host_effect_error(bound_predicate.rebind(predicate), obligation.param_env, span) } ty::PredicateKind::Subtype(predicate) => { @@ -763,6 +747,41 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { applied_do_not_recommend } + fn report_host_effect_error( + &self, + predicate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + ) -> Diag<'a> { + // FIXME(const_trait_impl): We should recompute the predicate with `~const` + // if it's `const`, and if it holds, explain that this bound only + // *conditionally* holds. If that fails, we should also do selection + // to drill this down to an impl or built-in source, so we can + // point at it and explain that while the trait *is* implemented, + // that implementation is not const. + let trait_ref = predicate.map_bound(|predicate| ty::TraitPredicate { + trait_ref: predicate.trait_ref, + polarity: ty::PredicatePolarity::Positive, + }); + let err_msg = self.get_standard_error_message( + trait_ref, + None, + Some(predicate.constness()), + None, + String::new(), + ); + let mut diag = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); + if !self.predicate_may_hold(&Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + trait_ref, + )) { + diag.downgrade_to_delayed_bug(); + } + diag + } + fn emit_specialized_closure_kind_error( &self, obligation: &PredicateObligation<'tcx>, diff --git a/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs new file mode 100644 index 0000000000000..f4b01efe95908 --- /dev/null +++ b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs @@ -0,0 +1,22 @@ +// Make sure we don't issue *two* error messages for the trait predicate *and* host predicate. + +#![feature(const_trait_impl)] + +#[const_trait] +trait Trait { + type Out; +} + +const fn needs_const(_: &T) {} + +const IN_CONST: () = { + needs_const(&()); + //~^ ERROR the trait bound `(): Trait` is not satisfied +}; + +const fn conditionally_const() { + needs_const(&()); + //~^ ERROR the trait bound `(): Trait` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr new file mode 100644 index 0000000000000..cd68cdaf8a2bf --- /dev/null +++ b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `(): Trait` is not satisfied + --> $DIR/double-error-for-unimplemented-trait.rs:13:15 + | +LL | needs_const(&()); + | ----------- ^^^ the trait `Trait` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/double-error-for-unimplemented-trait.rs:6:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ +note: required by a bound in `needs_const` + --> $DIR/double-error-for-unimplemented-trait.rs:10:25 + | +LL | const fn needs_const(_: &T) {} + | ^^^^^^^^^^^^ required by this bound in `needs_const` + +error[E0277]: the trait bound `(): Trait` is not satisfied + --> $DIR/double-error-for-unimplemented-trait.rs:18:15 + | +LL | needs_const(&()); + | ----------- ^^^ the trait `Trait` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/double-error-for-unimplemented-trait.rs:6:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ +note: required by a bound in `needs_const` + --> $DIR/double-error-for-unimplemented-trait.rs:10:25 + | +LL | const fn needs_const(_: &T) {} + | ^^^^^^^^^^^^ required by this bound in `needs_const` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 62d6ee3a26a9fbc2f0d99939bffd387452793856 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 31 Dec 2024 03:09:31 +0000 Subject: [PATCH 2/2] nit: what the heck is `o` --- .../src/error_reporting/traits/fulfillment_errors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 163c453bdbe7f..92aba24898800 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -114,7 +114,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // // We rely on a few heuristics to identify cases where this root // obligation is more important than the leaf obligation: - let (main_trait_predicate, o) = if let ty::PredicateKind::Clause( + let (main_trait_predicate, main_obligation) = if let ty::PredicateKind::Clause( ty::ClauseKind::Trait(root_pred) ) = root_obligation.predicate.kind().skip_binder() && !leaf_trait_predicate.self_ty().skip_binder().has_escaping_bound_vars() @@ -199,7 +199,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { notes, parent_label, append_const_msg, - } = self.on_unimplemented_note(main_trait_predicate, o, &mut long_ty_file); + } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file); let have_alt_message = message.is_some() || label.is_some(); let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id());