From 717c4817391f61953eed678607168cfea81e3111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 16 Jun 2023 18:09:54 +0000 Subject: [PATCH] Account for sealed traits in trait bound errors When implementing a public trait with a private super-trait, we now emit a note that the missing bound is not going to be able to be satisfied, and we explain the concept of a sealed trait. --- .../src/traits/error_reporting/suggestions.rs | 26 +++++++++++++++++++ .../sealed-traits/sealed-trait-local.rs | 19 ++++++++++++++ .../sealed-traits/sealed-trait-local.stderr | 16 ++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/ui/privacy/sealed-traits/sealed-trait-local.rs create mode 100644 tests/ui/privacy/sealed-traits/sealed-trait-local.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 966c4a7dcf3..dba6ffc4c96 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2724,6 +2724,32 @@ fn note_obligation_cause_code( let msg = format!("required by this bound in `{short_item_name}`"); multispan.push_span_label(span, msg); err.span_note(multispan, descr); + if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() + && let ty::ClauseKind::Trait(trait_pred) = clause + { + let def_id = trait_pred.def_id(); + let visible_item = if let Some(local) = def_id.as_local() { + // Check for local traits being reachable. + let vis = &self.tcx.resolutions(()).effective_visibilities; + // Account for non-`pub` traits in the root of the local crate. + let is_locally_reachable = self.tcx.parent(def_id).is_crate_root(); + vis.is_reachable(local) || is_locally_reachable + } else { + // Check for foreign traits being reachable. + self.tcx.visible_parent_map(()).get(&def_id).is_some() + }; + if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item { + // FIXME(estebank): extend this to search for all the types that do + // implement this trait and list them. + err.note(format!( + "`{short_item_name}` is a \"sealed trait\", because to implement \ + it you also need to implelement `{}`, which is not accessible; \ + this is usually done to force you to use one of the provided \ + types that already implement it", + with_no_trimmed_paths!(tcx.def_path_str(def_id)), + )); + } + } } else { err.span_note(tcx.def_span(item_def_id), descr); } diff --git a/tests/ui/privacy/sealed-traits/sealed-trait-local.rs b/tests/ui/privacy/sealed-traits/sealed-trait-local.rs new file mode 100644 index 00000000000..778ddf0f817 --- /dev/null +++ b/tests/ui/privacy/sealed-traits/sealed-trait-local.rs @@ -0,0 +1,19 @@ +// provide custom privacy error for sealed traits +pub mod a { + pub trait Sealed: self::b::Hidden { + fn foo() {} + } + + struct X; + impl Sealed for X {} + impl self::b::Hidden for X {} + + mod b { + pub trait Hidden {} + } +} + +struct S; +impl a::Sealed for S {} //~ ERROR the trait bound `S: Hidden` is not satisfied + +fn main() {} diff --git a/tests/ui/privacy/sealed-traits/sealed-trait-local.stderr b/tests/ui/privacy/sealed-traits/sealed-trait-local.stderr new file mode 100644 index 00000000000..d1052ce3508 --- /dev/null +++ b/tests/ui/privacy/sealed-traits/sealed-trait-local.stderr @@ -0,0 +1,16 @@ +error[E0277]: the trait bound `S: Hidden` is not satisfied + --> $DIR/sealed-trait-local.rs:17:20 + | +LL | impl a::Sealed for S {} + | ^ the trait `Hidden` is not implemented for `S` + | +note: required by a bound in `Sealed` + --> $DIR/sealed-trait-local.rs:3:23 + | +LL | pub trait Sealed: self::b::Hidden { + | ^^^^^^^^^^^^^^^ required by this bound in `Sealed` + = note: `Sealed` is a "sealed trait", because to implement it you also need to implelement `a::b::Hidden`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.